summaryrefslogtreecommitdiff
path: root/libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp')
-rw-r--r--libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp1353
1 files changed, 0 insertions, 1353 deletions
diff --git a/libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp b/libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp
deleted file mode 100644
index 93d3753..0000000
--- a/libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp
+++ /dev/null
@@ -1,1353 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2022, assimp team
-
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the following
-conditions are met:
-
-* Redistributions of source code must retain the above
- copyright notice, this list of conditions and the
- following disclaimer.
-
-* Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the
- following disclaimer in the documentation and/or other
- materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
- contributors may be used to endorse or promote products
- derived from this software without specific prior
- written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------
-*/
-
-/** @file HL1MDLLoader.cpp
- * @brief Implementation for the Half-Life 1 MDL loader.
- */
-
-#include "HL1MDLLoader.h"
-#include "HL1ImportDefinitions.h"
-#include "HL1MeshTrivert.h"
-#include "UniqueNameGenerator.h"
-
-#include <assimp/BaseImporter.h>
-#include <assimp/StringUtils.h>
-#include <assimp/ai_assert.h>
-#include <assimp/qnan.h>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/Importer.hpp>
-
-#include <iomanip>
-#include <sstream>
-#include <map>
-
-#ifdef MDL_HALFLIFE_LOG_WARN_HEADER
-#undef MDL_HALFLIFE_LOG_WARN_HEADER
-#endif
-#define MDL_HALFLIFE_LOG_HEADER "[Half-Life 1 MDL] "
-#include "LogFunctions.h"
-
-namespace Assimp {
-namespace MDL {
-namespace HalfLife {
-
-#ifdef _MSC_VER
-# pragma warning(disable : 4706)
-#endif // _MSC_VER
-
-// ------------------------------------------------------------------------------------------------
-HL1MDLLoader::HL1MDLLoader(
- aiScene *scene,
- IOSystem *io,
- const unsigned char *buffer,
- const std::string &file_path,
- const HL1ImportSettings &import_settings) :
- scene_(scene),
- io_(io),
- buffer_(buffer),
- file_path_(file_path),
- import_settings_(import_settings),
- header_(nullptr),
- texture_header_(nullptr),
- anim_headers_(nullptr),
- texture_buffer_(nullptr),
- anim_buffers_(nullptr),
- num_sequence_groups_(0),
- rootnode_children_(),
- unique_name_generator_(),
- unique_sequence_names_(),
- unique_sequence_groups_names_(),
- temp_bones_(),
- num_blend_controllers_(0),
- total_models_(0) {
- load_file();
-}
-
-// ------------------------------------------------------------------------------------------------
-HL1MDLLoader::~HL1MDLLoader() {
- release_resources();
-}
-
-// ------------------------------------------------------------------------------------------------
-void HL1MDLLoader::release_resources() {
- if (buffer_ != texture_buffer_) {
- delete[] texture_buffer_;
- texture_buffer_ = nullptr;
- }
-
- if (num_sequence_groups_ && anim_buffers_) {
- for (int i = 1; i < num_sequence_groups_; ++i) {
- if (anim_buffers_[i]) {
- delete[] anim_buffers_[i];
- anim_buffers_[i] = nullptr;
- }
- }
-
- delete[] anim_buffers_;
- anim_buffers_ = nullptr;
- }
-
- if (anim_headers_) {
- delete[] anim_headers_;
- anim_headers_ = nullptr;
- }
-
- // Root has some children nodes. so let's proceed them
- if (!rootnode_children_.empty()) {
- // Here, it means that the nodes were not added to the
- // scene root node. We still have to delete them.
- for (auto it = rootnode_children_.begin(); it != rootnode_children_.end(); ++it) {
- if (*it) {
- delete *it;
- }
- }
- // Ensure this happens only once.
- rootnode_children_.clear();
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-void HL1MDLLoader::load_file() {
- try {
- header_ = (const Header_HL1 *)buffer_;
- validate_header(header_, false);
-
- // Create the root scene node.
- scene_->mRootNode = new aiNode(AI_MDL_HL1_NODE_ROOT);
-
- load_texture_file();
-
- if (import_settings_.read_animations) {
- load_sequence_groups_files();
- }
-
- read_textures();
- read_skins();
-
- read_bones();
- read_meshes();
-
- if (import_settings_.read_animations) {
- read_sequence_groups_info();
- read_animations();
- read_sequence_infos();
- if (import_settings_.read_sequence_transitions)
- read_sequence_transitions();
- }
-
- if (import_settings_.read_attachments) {
- read_attachments();
- }
-
- if (import_settings_.read_hitboxes) {
- read_hitboxes();
- }
-
- if (import_settings_.read_bone_controllers) {
- read_bone_controllers();
- }
-
- read_global_info();
-
- if (!header_->numbodyparts) {
- // This could be an MDL external texture file. In this case,
- // add this flag to allow the scene to be loaded even if it
- // has no meshes.
- scene_->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
- }
-
- // Append children to root node.
- if (rootnode_children_.size()) {
- scene_->mRootNode->addChildren(
- static_cast<unsigned int>(rootnode_children_.size()),
- rootnode_children_.data());
-
- // Clear the list of nodes so they will not be destroyed
- // when resources are released.
- rootnode_children_.clear();
- }
-
- release_resources();
-
- } catch (...) {
- release_resources();
- throw;
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-void HL1MDLLoader::validate_header(const Header_HL1 *header, bool is_texture_header) {
- if (is_texture_header) {
- // Every single Half-Life model is assumed to have at least one texture.
- if (!header->numtextures) {
- throw DeadlyImportError(MDL_HALFLIFE_LOG_HEADER "There are no textures in the file");
- }
-
- if (header->numtextures > AI_MDL_HL1_MAX_TEXTURES) {
- log_warning_limit_exceeded<AI_MDL_HL1_MAX_TEXTURES>(header->numtextures, "textures");
- }
-
- if (header->numskinfamilies > AI_MDL_HL1_MAX_SKIN_FAMILIES) {
- log_warning_limit_exceeded<AI_MDL_HL1_MAX_SKIN_FAMILIES>(header->numskinfamilies, "skin families");
- }
-
- } else {
-
- if (header->numbodyparts > AI_MDL_HL1_MAX_BODYPARTS) {
- log_warning_limit_exceeded<AI_MDL_HL1_MAX_BODYPARTS>(header->numbodyparts, "bodyparts");
- }
-
- if (header->numbones > AI_MDL_HL1_MAX_BONES) {
- log_warning_limit_exceeded<AI_MDL_HL1_MAX_BONES>(header->numbones, "bones");
- }
-
- if (header->numbonecontrollers > AI_MDL_HL1_MAX_BONE_CONTROLLERS) {
- log_warning_limit_exceeded<AI_MDL_HL1_MAX_BONE_CONTROLLERS>(header->numbonecontrollers, "bone controllers");
- }
-
- if (header->numseq > AI_MDL_HL1_MAX_SEQUENCES) {
- log_warning_limit_exceeded<AI_MDL_HL1_MAX_SEQUENCES>(header->numseq, "sequences");
- }
-
- if (header->numseqgroups > AI_MDL_HL1_MAX_SEQUENCE_GROUPS) {
- log_warning_limit_exceeded<AI_MDL_HL1_MAX_SEQUENCE_GROUPS>(header->numseqgroups, "sequence groups");
- }
-
- if (header->numattachments > AI_MDL_HL1_MAX_ATTACHMENTS) {
- log_warning_limit_exceeded<AI_MDL_HL1_MAX_ATTACHMENTS>(header->numattachments, "attachments");
- }
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-/*
- Load textures.
-
- There are two ways for textures to be stored in a Half-Life model:
-
- 1. Directly in the MDL file (filePath) or
- 2. In an external MDL file.
-
- Due to the way StudioMDL works (tool used to compile SMDs into MDLs),
- it is assumed that an external texture file follows the naming
- convention: <YourModelName>T.mdl. Note the extra (T) at the end of the
- model name.
-
- .e.g For a given model named MyModel.mdl
-
- The external texture file name would be MyModelT.mdl
-*/
-void HL1MDLLoader::load_texture_file() {
- if (header_->numtextures == 0) {
- // Load an external MDL texture file.
- std::string texture_file_path =
- DefaultIOSystem::absolutePath(file_path_) + io_->getOsSeparator() +
- DefaultIOSystem::completeBaseName(file_path_) + "T." +
- BaseImporter::GetExtension(file_path_);
-
- load_file_into_buffer<Header_HL1>(texture_file_path, texture_buffer_);
- } else {
- // Model has no external texture file. This means the texture is stored inside the main MDL file.
- texture_buffer_ = const_cast<unsigned char *>(buffer_);
- }
-
- texture_header_ = (const Header_HL1 *)texture_buffer_;
-
- // Validate texture header.
- validate_header(texture_header_, true);
-}
-
-// ------------------------------------------------------------------------------------------------
-/*
- Load sequence group files if any.
-
- Due to the way StudioMDL works (tool used to compile SMDs into MDLs),
- it is assumed that a sequence group file follows the naming
- convention: <YourModelName>0X.mdl. Note the extra (0X) at the end of
- the model name, where (X) is the sequence group.
-
- .e.g For a given model named MyModel.mdl
-
- Sequence group 1 => MyModel01.mdl
- Sequence group 2 => MyModel02.mdl
- Sequence group X => MyModel0X.mdl
-
-*/
-void HL1MDLLoader::load_sequence_groups_files() {
- if (header_->numseqgroups <= 1) {
- return;
- }
-
- num_sequence_groups_ = header_->numseqgroups;
-
- anim_buffers_ = new unsigned char *[num_sequence_groups_];
- anim_headers_ = new SequenceHeader_HL1 *[num_sequence_groups_];
- for (int i = 0; i < num_sequence_groups_; ++i) {
- anim_buffers_[i] = nullptr;
- anim_headers_[i] = nullptr;
- }
-
- std::string file_path_without_extension =
- DefaultIOSystem::absolutePath(file_path_) +
- io_->getOsSeparator() +
- DefaultIOSystem::completeBaseName(file_path_);
-
- for (int i = 1; i < num_sequence_groups_; ++i) {
- std::stringstream ss;
- ss << file_path_without_extension;
- ss << std::setw(2) << std::setfill('0') << i;
- ss << '.' << BaseImporter::GetExtension(file_path_);
-
- std::string sequence_file_path = ss.str();
-
- load_file_into_buffer<SequenceHeader_HL1>(sequence_file_path, anim_buffers_[i]);
-
- anim_headers_[i] = (SequenceHeader_HL1 *)anim_buffers_[i];
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Read an MDL texture.
-void HL1MDLLoader::read_texture(const Texture_HL1 *ptexture,
- uint8_t *data, uint8_t *pal, aiTexture *pResult,
- aiColor3D &last_palette_color) {
- pResult->mFilename = ptexture->name;
- pResult->mWidth = static_cast<unsigned int>(ptexture->width);
- pResult->mHeight = static_cast<unsigned int>(ptexture->height);
- pResult->achFormatHint[0] = 'r';
- pResult->achFormatHint[1] = 'g';
- pResult->achFormatHint[2] = 'b';
- pResult->achFormatHint[3] = 'a';
- pResult->achFormatHint[4] = '8';
- pResult->achFormatHint[5] = '8';
- pResult->achFormatHint[6] = '8';
- pResult->achFormatHint[7] = '8';
- pResult->achFormatHint[8] = '\0';
-
- const size_t num_pixels = pResult->mWidth * pResult->mHeight;
- aiTexel *out = pResult->pcData = new aiTexel[num_pixels];
-
- // Convert indexed 8 bit to 32 bit RGBA.
- for (size_t i = 0; i < num_pixels; ++i, ++out) {
- out->r = pal[data[i] * 3];
- out->g = pal[data[i] * 3 + 1];
- out->b = pal[data[i] * 3 + 2];
- out->a = 255;
- }
-
- // Get the last palette color.
- last_palette_color.r = pal[255 * 3];
- last_palette_color.g = pal[255 * 3 + 1];
- last_palette_color.b = pal[255 * 3 + 2];
-}
-
-// ------------------------------------------------------------------------------------------------
-void HL1MDLLoader::read_textures() {
- const Texture_HL1 *ptexture = (const Texture_HL1 *)((uint8_t *)texture_header_ + texture_header_->textureindex);
- unsigned char *pin = texture_buffer_;
-
- scene_->mNumTextures = scene_->mNumMaterials = texture_header_->numtextures;
- scene_->mTextures = new aiTexture *[scene_->mNumTextures];
- scene_->mMaterials = new aiMaterial *[scene_->mNumMaterials];
-
- for (int i = 0; i < texture_header_->numtextures; ++i) {
- scene_->mTextures[i] = new aiTexture();
-
- aiColor3D last_palette_color;
- read_texture(&ptexture[i],
- pin + ptexture[i].index,
- pin + ptexture[i].width * ptexture[i].height + ptexture[i].index,
- scene_->mTextures[i],
- last_palette_color);
-
- aiMaterial *scene_material = scene_->mMaterials[i] = new aiMaterial();
-
- const aiTextureType texture_type = aiTextureType_DIFFUSE;
- aiString texture_name(ptexture[i].name);
- scene_material->AddProperty(&texture_name, AI_MATKEY_TEXTURE(texture_type, 0));
-
- // Is this a chrome texture?
- int chrome = ptexture[i].flags & AI_MDL_HL1_STUDIO_NF_CHROME ? 1 : 0;
- scene_material->AddProperty(&chrome, 1, AI_MDL_HL1_MATKEY_CHROME(texture_type, 0));
-
- if (ptexture[i].flags & AI_MDL_HL1_STUDIO_NF_FLATSHADE) {
- // Flat shading.
- const aiShadingMode shading_mode = aiShadingMode_Flat;
- scene_material->AddProperty(&shading_mode, 1, AI_MATKEY_SHADING_MODEL);
- }
-
- if (ptexture[i].flags & AI_MDL_HL1_STUDIO_NF_ADDITIVE) {
- // Additive texture.
- const aiBlendMode blend_mode = aiBlendMode_Additive;
- scene_material->AddProperty(&blend_mode, 1, AI_MATKEY_BLEND_FUNC);
- } else if (ptexture[i].flags & AI_MDL_HL1_STUDIO_NF_MASKED) {
- // Texture with 1 bit alpha test.
- const aiTextureFlags use_alpha = aiTextureFlags_UseAlpha;
- scene_material->AddProperty(&use_alpha, 1, AI_MATKEY_TEXFLAGS(texture_type, 0));
- scene_material->AddProperty(&last_palette_color, 1, AI_MATKEY_COLOR_TRANSPARENT);
- }
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-void HL1MDLLoader::read_skins() {
- // Read skins, if any.
- if (texture_header_->numskinfamilies <= 1) {
- return;
- }
-
- // Pointer to base texture index.
- short *default_skin_ptr = (short *)((uint8_t *)texture_header_ + texture_header_->skinindex);
-
- // Start at first replacement skin.
- short *replacement_skin_ptr = default_skin_ptr + texture_header_->numskinref;
-
- for (int i = 1; i < texture_header_->numskinfamilies; ++i, replacement_skin_ptr += texture_header_->numskinref) {
- for (int j = 0; j < texture_header_->numskinref; ++j) {
- if (default_skin_ptr[j] != replacement_skin_ptr[j]) {
- // Save replacement textures.
- aiString skinMaterialId(scene_->mTextures[replacement_skin_ptr[j]]->mFilename);
- scene_->mMaterials[default_skin_ptr[j]]->AddProperty(&skinMaterialId, AI_MATKEY_TEXTURE_DIFFUSE(i));
- }
- }
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-void HL1MDLLoader::read_bones() {
- if (!header_->numbones) {
- return;
- }
-
- const Bone_HL1 *pbone = (const Bone_HL1 *)((uint8_t *)header_ + header_->boneindex);
-
- std::vector<std::string> unique_bones_names(header_->numbones);
- for (int i = 0; i < header_->numbones; ++i) {
- unique_bones_names[i] = pbone[i].name;
- }
-
- // Ensure bones have unique names.
- unique_name_generator_.set_template_name("Bone");
- unique_name_generator_.make_unique(unique_bones_names);
-
- temp_bones_.resize(header_->numbones);
-
- aiNode *bones_node = new aiNode(AI_MDL_HL1_NODE_BONES);
- rootnode_children_.push_back(bones_node);
- bones_node->mNumChildren = static_cast<unsigned int>(header_->numbones);
- bones_node->mChildren = new aiNode *[bones_node->mNumChildren];
-
- // Create bone matrices in local space.
- for (int i = 0; i < header_->numbones; ++i) {
- aiNode *bone_node = temp_bones_[i].node = bones_node->mChildren[i] = new aiNode(unique_bones_names[i]);
-
- aiVector3D angles(pbone[i].value[3], pbone[i].value[4], pbone[i].value[5]);
- temp_bones_[i].absolute_transform = bone_node->mTransformation =
- aiMatrix4x4(aiVector3D(1), aiQuaternion(angles.y, angles.z, angles.x),
- aiVector3D(pbone[i].value[0], pbone[i].value[1], pbone[i].value[2]));
-
- if (pbone[i].parent == -1) {
- bone_node->mParent = scene_->mRootNode;
- } else {
- bone_node->mParent = bones_node->mChildren[pbone[i].parent];
-
- temp_bones_[i].absolute_transform =
- temp_bones_[pbone[i].parent].absolute_transform * bone_node->mTransformation;
- }
-
- temp_bones_[i].offset_matrix = temp_bones_[i].absolute_transform;
- temp_bones_[i].offset_matrix.Inverse();
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-/*
- Read meshes.
-
- Half-Life MDLs are structured such that each MDL
- contains one or more 'bodypart(s)', which contain one
- or more 'model(s)', which contains one or more mesh(es).
-
- * Bodyparts are used to group models that may be replaced
- in the game .e.g a character could have a 'heads' group,
- 'torso' group, 'shoes' group, with each group containing
- different 'model(s)'.
-
- * Models, also called 'sub models', contain vertices as
- well as a reference to each mesh used by the sub model.
-
- * Meshes contain a list of tris, also known as 'triverts'.
- Each tris contains the following information:
-
- 1. The index of the position to use for the vertex.
- 2. The index of the normal to use for the vertex.
- 3. The S coordinate to use for the vertex UV.
- 4. The T coordinate ^
-
- These tris represent the way to represent the triangles
- for each mesh. Depending on how the tool compiled the MDL,
- those triangles were saved as strips and or fans.
-
- NOTE: Each tris is NOT unique. This means that you
- might encounter the same vertex index but with a different
- normal index, S coordinate, T coordinate.
-
- In addition, each mesh contains the texture's index.
-
- ------------------------------------------------------
- With the details above, there are several things to
- take into consideration.
-
- * The Half-Life models store the vertices by sub model
- rather than by mesh. Due to Assimp's structure, it
- is necessary to remap each model vertex to be used
- per mesh. Unfortunately, this has the consequence
- to duplicate vertices.
-
- * Because the mesh triangles are comprised of strips and
- fans, it is necessary to convert each primitive to
- triangles, respectively (3 indices per face).
-*/
-void HL1MDLLoader::read_meshes() {
- if (!header_->numbodyparts) {
- return;
- }
-
- int total_verts = 0;
- int total_triangles = 0;
- total_models_ = 0;
-
- const Bodypart_HL1 *pbodypart = (const Bodypart_HL1 *)((uint8_t *)header_ + header_->bodypartindex);
- const Model_HL1 *pmodel = nullptr;
- const Mesh_HL1 *pmesh = nullptr;
-
- const Texture_HL1 *ptexture = (const Texture_HL1 *)((uint8_t *)texture_header_ + texture_header_->textureindex);
- short *pskinref = (short *)((uint8_t *)texture_header_ + texture_header_->skinindex);
-
- scene_->mNumMeshes = 0;
-
- std::vector<std::string> unique_bodyparts_names;
- unique_bodyparts_names.resize(header_->numbodyparts);
-
- // Count the number of meshes.
-
- for (int i = 0; i < header_->numbodyparts; ++i, ++pbodypart) {
- unique_bodyparts_names[i] = pbodypart->name;
-
- pmodel = (Model_HL1 *)((uint8_t *)header_ + pbodypart->modelindex);
- for (int j = 0; j < pbodypart->nummodels; ++j, ++pmodel) {
- scene_->mNumMeshes += pmodel->nummesh;
- total_verts += pmodel->numverts;
- }
-
- total_models_ += pbodypart->nummodels;
- }
-
- // Display limit infos.
- if (total_verts > AI_MDL_HL1_MAX_VERTICES) {
- log_warning_limit_exceeded<AI_MDL_HL1_MAX_VERTICES>(total_verts, "vertices");
- }
-
- if (scene_->mNumMeshes > AI_MDL_HL1_MAX_MESHES) {
- log_warning_limit_exceeded<AI_MDL_HL1_MAX_MESHES>(scene_->mNumMeshes, "meshes");
- }
-
- if (total_models_ > AI_MDL_HL1_MAX_MODELS) {
- log_warning_limit_exceeded<AI_MDL_HL1_MAX_MODELS>(total_models_, "models");
- }
-
- // Ensure bodyparts have unique names.
- unique_name_generator_.set_template_name("Bodypart");
- unique_name_generator_.make_unique(unique_bodyparts_names);
-
- // Now do the same for each model.
- pbodypart = (const Bodypart_HL1 *)((uint8_t *)header_ + header_->bodypartindex);
-
- // Prepare template name for bodypart models.
- std::vector<std::string> unique_models_names;
- unique_models_names.resize(total_models_);
-
- unsigned int model_index = 0;
-
- for (int i = 0; i < header_->numbodyparts; ++i, ++pbodypart) {
- pmodel = (Model_HL1 *)((uint8_t *)header_ + pbodypart->modelindex);
- for (int j = 0; j < pbodypart->nummodels; ++j, ++pmodel, ++model_index)
- unique_models_names[model_index] = pmodel->name;
- }
-
- unique_name_generator_.set_template_name("Model");
- unique_name_generator_.make_unique(unique_models_names);
-
- unsigned int mesh_index = 0;
-
- scene_->mMeshes = new aiMesh *[scene_->mNumMeshes];
-
- pbodypart = (const Bodypart_HL1 *)((uint8_t *)header_ + header_->bodypartindex);
-
- /* Create a node that will represent the mesh hierarchy.
-
- <MDL_bodyparts>
- |
- +-- bodypart --+-- model -- [mesh index, mesh index, ...]
- | |
- | +-- model -- [mesh index, mesh index, ...]
- | |
- | ...
- |
- |-- bodypart -- ...
- |
- ...
- */
- aiNode *bodyparts_node = new aiNode(AI_MDL_HL1_NODE_BODYPARTS);
- rootnode_children_.push_back(bodyparts_node);
- bodyparts_node->mNumChildren = static_cast<unsigned int>(header_->numbodyparts);
- bodyparts_node->mChildren = new aiNode *[bodyparts_node->mNumChildren];
- aiNode **bodyparts_node_ptr = bodyparts_node->mChildren;
-
- // The following variables are defined here so they don't have
- // to be recreated every iteration.
-
- // Model_HL1 vertices, in bind pose space.
- std::vector<aiVector3D> bind_pose_vertices;
-
- // Model_HL1 normals, in bind pose space.
- std::vector<aiVector3D> bind_pose_normals;
-
- // Used to contain temporary information for building a mesh.
- std::vector<HL1MeshTrivert> triverts;
-
- std::vector<short> tricmds;
-
- // Which triverts to use for the mesh.
- std::vector<short> mesh_triverts_indices;
-
- std::vector<HL1MeshFace> mesh_faces;
-
- /* triverts that have the same vertindex, but have different normindex,s,t values.
- Similar triverts are mapped from vertindex to a list of similar triverts. */
- std::map<short, std::set<short>> triverts_similars;
-
- // triverts per bone.
- std::map<int, std::set<short>> bone_triverts;
-
- /** This function adds a trivert index to the list of triverts per bone.
- * \param[in] bone The bone that affects the trivert at index \p trivert_index.
- * \param[in] trivert_index The trivert index.
- */
- auto AddTrivertToBone = [&](int bone, short trivert_index) {
- if (bone_triverts.count(bone) == 0)
- bone_triverts.insert({ bone, std::set<short>{ trivert_index }});
- else
- bone_triverts[bone].insert(trivert_index);
- };
-
- /** This function creates and appends a new trivert to the list of triverts.
- * \param[in] trivert The trivert to use as a prototype.
- * \param[in] bone The bone that affects \p trivert.
- */
- auto AddSimilarTrivert = [&](const Trivert &trivert, const int bone) {
- HL1MeshTrivert new_trivert(trivert);
- new_trivert.localindex = static_cast<short>(mesh_triverts_indices.size());
-
- short new_trivert_index = static_cast<short>(triverts.size());
-
- if (triverts_similars.count(trivert.vertindex) == 0)
- triverts_similars.insert({ trivert.vertindex, std::set<short>{ new_trivert_index }});
- else
- triverts_similars[trivert.vertindex].insert(new_trivert_index);
-
- triverts.push_back(new_trivert);
-
- mesh_triverts_indices.push_back(new_trivert_index);
- tricmds.push_back(new_trivert.localindex);
- AddTrivertToBone(bone, new_trivert.localindex);
- };
-
- model_index = 0;
-
- for (int i = 0; i < header_->numbodyparts; ++i, ++pbodypart, ++bodyparts_node_ptr) {
- pmodel = (const Model_HL1 *)((uint8_t *)header_ + pbodypart->modelindex);
-
- // Create bodypart node for the mesh tree hierarchy.
- aiNode *bodypart_node = (*bodyparts_node_ptr) = new aiNode(unique_bodyparts_names[i]);
- bodypart_node->mParent = bodyparts_node;
- bodypart_node->mMetaData = aiMetadata::Alloc(1);
- bodypart_node->mMetaData->Set(0, "Base", pbodypart->base);
-
- bodypart_node->mNumChildren = static_cast<unsigned int>(pbodypart->nummodels);
- bodypart_node->mChildren = new aiNode *[bodypart_node->mNumChildren];
- aiNode **bodypart_models_ptr = bodypart_node->mChildren;
-
- for (int j = 0; j < pbodypart->nummodels;
- ++j, ++pmodel, ++bodypart_models_ptr, ++model_index) {
-
- pmesh = (const Mesh_HL1 *)((uint8_t *)header_ + pmodel->meshindex);
-
- uint8_t *pvertbone = ((uint8_t *)header_ + pmodel->vertinfoindex);
- uint8_t *pnormbone = ((uint8_t *)header_ + pmodel->norminfoindex);
- vec3_t *pstudioverts = (vec3_t *)((uint8_t *)header_ + pmodel->vertindex);
- vec3_t *pstudionorms = (vec3_t *)((uint8_t *)header_ + pmodel->normindex);
-
- // Each vertex and normal is in local space, so transform
- // each of them to bring them in bind pose.
- bind_pose_vertices.resize(pmodel->numverts);
- bind_pose_normals.resize(pmodel->numnorms);
- for (size_t k = 0; k < bind_pose_vertices.size(); ++k) {
- const vec3_t &vert = pstudioverts[k];
- bind_pose_vertices[k] = temp_bones_[pvertbone[k]].absolute_transform * aiVector3D(vert[0], vert[1], vert[2]);
- }
- for (size_t k = 0; k < bind_pose_normals.size(); ++k) {
- const vec3_t &norm = pstudionorms[k];
- // Compute the normal matrix to transform the normal into bind pose,
- // without affecting its length.
- const aiMatrix4x4 normal_matrix = aiMatrix4x4(temp_bones_[pnormbone[k]].absolute_transform).Inverse().Transpose();
- bind_pose_normals[k] = normal_matrix * aiVector3D(norm[0], norm[1], norm[2]);
- }
-
- // Create model node for the mesh tree hierarchy.
- aiNode *model_node = (*bodypart_models_ptr) = new aiNode(unique_models_names[model_index]);
- model_node->mParent = bodypart_node;
- model_node->mNumMeshes = static_cast<unsigned int>(pmodel->nummesh);
- model_node->mMeshes = new unsigned int[model_node->mNumMeshes];
- unsigned int *model_meshes_ptr = model_node->mMeshes;
-
- for (int k = 0; k < pmodel->nummesh; ++k, ++pmesh, ++mesh_index, ++model_meshes_ptr) {
- *model_meshes_ptr = mesh_index;
-
- // Read triverts.
- short *ptricmds = (short *)((uint8_t *)header_ + pmesh->triindex);
- float texcoords_s_scale = 1.0f / (float)ptexture[pskinref[pmesh->skinref]].width;
- float texcoords_t_scale = 1.0f / (float)ptexture[pskinref[pmesh->skinref]].height;
-
- // Reset the data for the upcoming mesh.
- triverts.clear();
- triverts.resize(pmodel->numverts);
- mesh_triverts_indices.clear();
- mesh_faces.clear();
- triverts_similars.clear();
- bone_triverts.clear();
-
- int l;
- while ((l = *(ptricmds++))) {
- bool is_triangle_fan = false;
-
- if (l < 0) {
- l = -l;
- is_triangle_fan = true;
- }
-
- // Clear the list of tris for the upcoming tris.
- tricmds.clear();
-
- for (; l > 0; l--, ptricmds += 4) {
- const Trivert *input_trivert = reinterpret_cast<const Trivert *>(ptricmds);
- const int bone = pvertbone[input_trivert->vertindex];
-
- HL1MeshTrivert *private_trivert = &triverts[input_trivert->vertindex];
- if (private_trivert->localindex == -1) {
- // First time referenced.
- *private_trivert = *input_trivert;
- private_trivert->localindex = static_cast<short>(mesh_triverts_indices.size());
- mesh_triverts_indices.push_back(input_trivert->vertindex);
- tricmds.push_back(private_trivert->localindex);
- AddTrivertToBone(bone, private_trivert->localindex);
- } else if (*private_trivert == *input_trivert) {
- // Exists and is the same.
- tricmds.push_back(private_trivert->localindex);
- } else {
- // No similar trivert associated to the trivert currently processed.
- if (triverts_similars.count(input_trivert->vertindex) == 0)
- AddSimilarTrivert(*input_trivert, bone);
- else {
- // Search in the list of similar triverts to see if the
- // trivert in process is already registered.
- short similar_index = -1;
- for (auto it = triverts_similars[input_trivert->vertindex].cbegin();
- similar_index == -1 && it != triverts_similars[input_trivert->vertindex].cend();
- ++it) {
- if (triverts[*it] == *input_trivert)
- similar_index = *it;
- }
-
- // If a similar trivert has been found, reuse it.
- // Otherwise, add it.
- if (similar_index == -1)
- AddSimilarTrivert(*input_trivert, bone);
- else
- tricmds.push_back(triverts[similar_index].localindex);
- }
- }
- }
-
- // Build mesh faces.
- const int num_faces = static_cast<int>(tricmds.size() - 2);
- mesh_faces.reserve(num_faces);
-
- if (is_triangle_fan) {
- for (int faceIdx = 0; faceIdx < num_faces; ++faceIdx) {
- mesh_faces.push_back(HL1MeshFace{
- tricmds[0],
- tricmds[faceIdx + 1],
- tricmds[faceIdx + 2] });
- }
- } else {
- for (int faceIdx = 0; faceIdx < num_faces; ++faceIdx) {
- if (faceIdx & 1) {
- // Preserve winding order.
- mesh_faces.push_back(HL1MeshFace{
- tricmds[faceIdx + 1],
- tricmds[faceIdx],
- tricmds[faceIdx + 2] });
- } else {
- mesh_faces.push_back(HL1MeshFace{
- tricmds[faceIdx],
- tricmds[faceIdx + 1],
- tricmds[faceIdx + 2] });
- }
- }
- }
-
- total_triangles += num_faces;
- }
-
- // Create the scene mesh.
- aiMesh *scene_mesh = scene_->mMeshes[mesh_index] = new aiMesh();
- scene_mesh->mPrimitiveTypes = aiPrimitiveType::aiPrimitiveType_TRIANGLE;
- scene_mesh->mMaterialIndex = pskinref[pmesh->skinref];
-
- scene_mesh->mNumVertices = static_cast<unsigned int>(mesh_triverts_indices.size());
-
- if (scene_mesh->mNumVertices) {
- scene_mesh->mVertices = new aiVector3D[scene_mesh->mNumVertices];
- scene_mesh->mNormals = new aiVector3D[scene_mesh->mNumVertices];
-
- scene_mesh->mNumUVComponents[0] = 2;
- scene_mesh->mTextureCoords[0] = new aiVector3D[scene_mesh->mNumVertices];
-
- // Add vertices.
- for (unsigned int v = 0; v < scene_mesh->mNumVertices; ++v) {
- const HL1MeshTrivert *pTrivert = &triverts[mesh_triverts_indices[v]];
- scene_mesh->mVertices[v] = bind_pose_vertices[pTrivert->vertindex];
- scene_mesh->mNormals[v] = bind_pose_normals[pTrivert->normindex];
- scene_mesh->mTextureCoords[0][v] = aiVector3D(
- pTrivert->s * texcoords_s_scale,
- pTrivert->t * -texcoords_t_scale, 0);
- }
-
- // Add face and indices.
- scene_mesh->mNumFaces = static_cast<unsigned int>(mesh_faces.size());
- scene_mesh->mFaces = new aiFace[scene_mesh->mNumFaces];
-
- for (unsigned int f = 0; f < scene_mesh->mNumFaces; ++f) {
- aiFace *face = &scene_mesh->mFaces[f];
- face->mNumIndices = 3;
- face->mIndices = new unsigned int[3];
- face->mIndices[0] = mesh_faces[f].v2;
- face->mIndices[1] = mesh_faces[f].v1;
- face->mIndices[2] = mesh_faces[f].v0;
- }
-
- // Add mesh bones.
- scene_mesh->mNumBones = static_cast<unsigned int>(bone_triverts.size());
- scene_mesh->mBones = new aiBone *[scene_mesh->mNumBones];
-
- aiBone **scene_bone_ptr = scene_mesh->mBones;
-
- for (auto bone_it = bone_triverts.cbegin();
- bone_it != bone_triverts.cend();
- ++bone_it, ++scene_bone_ptr) {
- const int bone_index = bone_it->first;
-
- aiBone *scene_bone = (*scene_bone_ptr) = new aiBone();
- scene_bone->mName = temp_bones_[bone_index].node->mName;
-
- scene_bone->mOffsetMatrix = temp_bones_[bone_index].offset_matrix;
-
- auto vertex_ids = bone_triverts.at(bone_index);
-
- // Add vertex weight per bone.
- scene_bone->mNumWeights = static_cast<unsigned int>(vertex_ids.size());
- aiVertexWeight *vertex_weight_ptr = scene_bone->mWeights = new aiVertexWeight[scene_bone->mNumWeights];
-
- for (auto vertex_it = vertex_ids.begin();
- vertex_it != vertex_ids.end();
- ++vertex_it, ++vertex_weight_ptr) {
- vertex_weight_ptr->mVertexId = *vertex_it;
- vertex_weight_ptr->mWeight = 1.0f;
- }
- }
- }
- }
- }
- }
-
- if (total_triangles > AI_MDL_HL1_MAX_TRIANGLES) {
- log_warning_limit_exceeded<AI_MDL_HL1_MAX_TRIANGLES>(total_triangles, "triangles");
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-void HL1MDLLoader::read_animations() {
- if (!header_->numseq) {
- return;
- }
-
- const SequenceDesc_HL1 *pseqdesc = (const SequenceDesc_HL1 *)((uint8_t *)header_ + header_->seqindex);
- const SequenceGroup_HL1 *pseqgroup = nullptr;
- const AnimValueOffset_HL1 *panim = nullptr;
- const AnimValue_HL1 *panimvalue = nullptr;
-
- unique_sequence_names_.resize(header_->numseq);
- for (int i = 0; i < header_->numseq; ++i)
- unique_sequence_names_[i] = pseqdesc[i].label;
-
- // Ensure sequences have unique names.
- unique_name_generator_.set_template_name("Sequence");
- unique_name_generator_.make_unique(unique_sequence_names_);
-
- scene_->mNumAnimations = 0;
-
- int highest_num_blend_animations = SequenceBlendMode_HL1::NoBlend;
-
- // Count the total number of animations.
- for (int i = 0; i < header_->numseq; ++i, ++pseqdesc) {
- scene_->mNumAnimations += pseqdesc->numblends;
- highest_num_blend_animations = std::max(pseqdesc->numblends, highest_num_blend_animations);
- }
-
- // Get the number of available blend controllers for global info.
- get_num_blend_controllers(highest_num_blend_animations, num_blend_controllers_);
-
- pseqdesc = (const SequenceDesc_HL1 *)((uint8_t *)header_ + header_->seqindex);
-
- aiAnimation **scene_animations_ptr = scene_->mAnimations = new aiAnimation *[scene_->mNumAnimations];
-
- for (int sequence = 0; sequence < header_->numseq; ++sequence, ++pseqdesc) {
- pseqgroup = (const SequenceGroup_HL1 *)((uint8_t *)header_ + header_->seqgroupindex) + pseqdesc->seqgroup;
-
- if (pseqdesc->seqgroup == 0) {
- panim = (const AnimValueOffset_HL1 *)((uint8_t *)header_ + pseqgroup->unused2 + pseqdesc->animindex);
- } else {
- panim = (const AnimValueOffset_HL1 *)((uint8_t *)anim_headers_[pseqdesc->seqgroup] + pseqdesc->animindex);
- }
-
- for (int blend = 0; blend < pseqdesc->numblends; ++blend, ++scene_animations_ptr) {
-
- const Bone_HL1 *pbone = (const Bone_HL1 *)((uint8_t *)header_ + header_->boneindex);
-
- aiAnimation *scene_animation = (*scene_animations_ptr) = new aiAnimation();
-
- scene_animation->mName = unique_sequence_names_[sequence];
- scene_animation->mTicksPerSecond = pseqdesc->fps;
- scene_animation->mDuration = static_cast<double>(pseqdesc->fps) * pseqdesc->numframes;
- scene_animation->mNumChannels = static_cast<unsigned int>(header_->numbones);
- scene_animation->mChannels = new aiNodeAnim *[scene_animation->mNumChannels];
-
- for (int bone = 0; bone < header_->numbones; bone++, ++pbone, ++panim) {
- aiNodeAnim *node_anim = scene_animation->mChannels[bone] = new aiNodeAnim();
- node_anim->mNodeName = temp_bones_[bone].node->mName;
-
- node_anim->mNumPositionKeys = pseqdesc->numframes;
- node_anim->mNumRotationKeys = node_anim->mNumPositionKeys;
- node_anim->mNumScalingKeys = 0;
-
- node_anim->mPositionKeys = new aiVectorKey[node_anim->mNumPositionKeys];
- node_anim->mRotationKeys = new aiQuatKey[node_anim->mNumRotationKeys];
-
- for (int frame = 0; frame < pseqdesc->numframes; ++frame) {
- aiVectorKey *position_key = &node_anim->mPositionKeys[frame];
- aiQuatKey *rotation_key = &node_anim->mRotationKeys[frame];
-
- aiVector3D angle1;
- for (int j = 0; j < 3; ++j) {
- if (panim->offset[j + 3] != 0) {
- // Read compressed rotation delta.
- panimvalue = (const AnimValue_HL1 *)((uint8_t *)panim + panim->offset[j + 3]);
- extract_anim_value(panimvalue, frame, pbone->scale[j + 3], angle1[j]);
- }
-
- // Add the default rotation value.
- angle1[j] += pbone->value[j + 3];
-
- if (panim->offset[j] != 0) {
- // Read compressed position delta.
- panimvalue = (const AnimValue_HL1 *)((uint8_t *)panim + panim->offset[j]);
- extract_anim_value(panimvalue, frame, pbone->scale[j], position_key->mValue[j]);
- }
-
- // Add the default position value.
- position_key->mValue[j] += pbone->value[j];
- }
-
- position_key->mTime = rotation_key->mTime = static_cast<double>(frame);
- /* The Half-Life engine uses X as forward, Y as left, Z as up. Therefore,
- pitch,yaw,roll is represented as (YZX). */
- rotation_key->mValue = aiQuaternion(angle1.y, angle1.z, angle1.x);
- rotation_key->mValue.Normalize();
- }
- }
- }
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-void HL1MDLLoader::read_sequence_groups_info() {
- if (!header_->numseqgroups) {
- return;
- }
-
- aiNode *sequence_groups_node = new aiNode(AI_MDL_HL1_NODE_SEQUENCE_GROUPS);
- rootnode_children_.push_back(sequence_groups_node);
-
- sequence_groups_node->mNumChildren = static_cast<unsigned int>(header_->numseqgroups);
- sequence_groups_node->mChildren = new aiNode *[sequence_groups_node->mNumChildren];
-
- const SequenceGroup_HL1 *pseqgroup = (const SequenceGroup_HL1 *)((uint8_t *)header_ + header_->seqgroupindex);
-
- unique_sequence_groups_names_.resize(header_->numseqgroups);
- for (int i = 0; i < header_->numseqgroups; ++i) {
- unique_sequence_groups_names_[i] = pseqgroup[i].label;
- }
-
- // Ensure sequence groups have unique names.
- unique_name_generator_.set_template_name("SequenceGroup");
- unique_name_generator_.make_unique(unique_sequence_groups_names_);
-
- for (int i = 0; i < header_->numseqgroups; ++i, ++pseqgroup) {
- aiNode *sequence_group_node = sequence_groups_node->mChildren[i] = new aiNode(unique_sequence_groups_names_[i]);
- sequence_group_node->mParent = sequence_groups_node;
-
- aiMetadata *md = sequence_group_node->mMetaData = aiMetadata::Alloc(1);
- if (i == 0) {
- /* StudioMDL does not write the file name for the default sequence group,
- so we will write it. */
- md->Set(0, "File", aiString(file_path_));
- } else {
- md->Set(0, "File", aiString(pseqgroup->name));
- }
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-void HL1MDLLoader::read_sequence_infos() {
- if (!header_->numseq) {
- return;
- }
-
- const SequenceDesc_HL1 *pseqdesc = (const SequenceDesc_HL1 *)((uint8_t *)header_ + header_->seqindex);
-
- aiNode *sequence_infos_node = new aiNode(AI_MDL_HL1_NODE_SEQUENCE_INFOS);
- rootnode_children_.push_back(sequence_infos_node);
-
- sequence_infos_node->mNumChildren = static_cast<unsigned int>(header_->numseq);
- sequence_infos_node->mChildren = new aiNode *[sequence_infos_node->mNumChildren];
-
- std::vector<aiNode *> sequence_info_node_children;
-
- int animation_index = 0;
- for (int i = 0; i < header_->numseq; ++i, ++pseqdesc) {
- // Clear the list of children for the upcoming sequence info node.
- sequence_info_node_children.clear();
-
- aiNode *sequence_info_node = sequence_infos_node->mChildren[i] = new aiNode(unique_sequence_names_[i]);
- sequence_info_node->mParent = sequence_infos_node;
-
- // Setup sequence info node Metadata.
- aiMetadata *md = sequence_info_node->mMetaData = aiMetadata::Alloc(16);
- md->Set(0, "AnimationIndex", animation_index);
- animation_index += pseqdesc->numblends;
-
- // Reference the sequence group by name. This allows us to search a particular
- // sequence group by name using aiNode(s).
- md->Set(1, "SequenceGroup", aiString(unique_sequence_groups_names_[pseqdesc->seqgroup]));
- md->Set(2, "FramesPerSecond", pseqdesc->fps);
- md->Set(3, "NumFrames", pseqdesc->numframes);
- md->Set(4, "NumBlends", pseqdesc->numblends);
- md->Set(5, "Activity", pseqdesc->activity);
- md->Set(6, "ActivityWeight", pseqdesc->actweight);
- md->Set(7, "MotionFlags", pseqdesc->motiontype);
- md->Set(8, "MotionBone", temp_bones_[pseqdesc->motionbone].node->mName);
- md->Set(9, "LinearMovement", aiVector3D(pseqdesc->linearmovement[0], pseqdesc->linearmovement[1], pseqdesc->linearmovement[2]));
- md->Set(10, "BBMin", aiVector3D(pseqdesc->bbmin[0], pseqdesc->bbmin[1], pseqdesc->bbmin[2]));
- md->Set(11, "BBMax", aiVector3D(pseqdesc->bbmax[0], pseqdesc->bbmax[1], pseqdesc->bbmax[2]));
- md->Set(12, "EntryNode", pseqdesc->entrynode);
- md->Set(13, "ExitNode", pseqdesc->exitnode);
- md->Set(14, "NodeFlags", pseqdesc->nodeflags);
- md->Set(15, "Flags", pseqdesc->flags);
-
- if (import_settings_.read_blend_controllers) {
- int num_blend_controllers;
- if (get_num_blend_controllers(pseqdesc->numblends, num_blend_controllers) && num_blend_controllers) {
- // Read blend controllers info.
- aiNode *blend_controllers_node = new aiNode(AI_MDL_HL1_NODE_BLEND_CONTROLLERS);
- sequence_info_node_children.push_back(blend_controllers_node);
- blend_controllers_node->mParent = sequence_info_node;
- blend_controllers_node->mNumChildren = static_cast<unsigned int>(num_blend_controllers);
- blend_controllers_node->mChildren = new aiNode *[blend_controllers_node->mNumChildren];
-
- for (unsigned int j = 0; j < blend_controllers_node->mNumChildren; ++j) {
- aiNode *blend_controller_node = blend_controllers_node->mChildren[j] = new aiNode();
- blend_controller_node->mParent = blend_controllers_node;
-
- aiMetadata *metaData = blend_controller_node->mMetaData = aiMetadata::Alloc(3);
- metaData->Set(0, "Start", pseqdesc->blendstart[j]);
- metaData->Set(1, "End", pseqdesc->blendend[j]);
- metaData->Set(2, "MotionFlags", pseqdesc->blendtype[j]);
- }
- }
- }
-
- if (import_settings_.read_animation_events && pseqdesc->numevents) {
- // Read animation events.
-
- if (pseqdesc->numevents > AI_MDL_HL1_MAX_EVENTS) {
- log_warning_limit_exceeded<AI_MDL_HL1_MAX_EVENTS>(
- "Sequence " + std::string(pseqdesc->label),
- pseqdesc->numevents, "animation events");
- }
-
- const AnimEvent_HL1 *pevent = (const AnimEvent_HL1 *)((uint8_t *)header_ + pseqdesc->eventindex);
-
- aiNode *pEventsNode = new aiNode(AI_MDL_HL1_NODE_ANIMATION_EVENTS);
- sequence_info_node_children.push_back(pEventsNode);
- pEventsNode->mParent = sequence_info_node;
- pEventsNode->mNumChildren = static_cast<unsigned int>(pseqdesc->numevents);
- pEventsNode->mChildren = new aiNode *[pEventsNode->mNumChildren];
-
- for (unsigned int j = 0; j < pEventsNode->mNumChildren; ++j, ++pevent) {
- aiNode *pEvent = pEventsNode->mChildren[j] = new aiNode();
- pEvent->mParent = pEventsNode;
-
- aiMetadata *metaData = pEvent->mMetaData = aiMetadata::Alloc(3);
- metaData->Set(0, "Frame", pevent->frame);
- metaData->Set(1, "ScriptEvent", pevent->event);
- metaData->Set(2, "Options", aiString(pevent->options));
- }
- }
-
- if (sequence_info_node_children.size()) {
- sequence_info_node->addChildren(
- static_cast<unsigned int>(sequence_info_node_children.size()),
- sequence_info_node_children.data());
- }
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-void HL1MDLLoader::read_sequence_transitions() {
- if (!header_->numtransitions) {
- return;
- }
-
- // Read sequence transition graph.
- aiNode *transition_graph_node = new aiNode(AI_MDL_HL1_NODE_SEQUENCE_TRANSITION_GRAPH);
- rootnode_children_.push_back(transition_graph_node);
-
- uint8_t *ptransitions = ((uint8_t *)header_ + header_->transitionindex);
- aiMetadata *md = transition_graph_node->mMetaData = aiMetadata::Alloc(header_->numtransitions * header_->numtransitions);
- for (unsigned int i = 0; i < md->mNumProperties; ++i)
- md->Set(i, std::to_string(i), static_cast<int>(ptransitions[i]));
-}
-
-void HL1MDLLoader::read_attachments() {
- if (!header_->numattachments) {
- return;
- }
-
- const Attachment_HL1 *pattach = (const Attachment_HL1 *)((uint8_t *)header_ + header_->attachmentindex);
-
- aiNode *attachments_node = new aiNode(AI_MDL_HL1_NODE_ATTACHMENTS);
- rootnode_children_.push_back(attachments_node);
- attachments_node->mNumChildren = static_cast<unsigned int>(header_->numattachments);
- attachments_node->mChildren = new aiNode *[attachments_node->mNumChildren];
-
- for (int i = 0; i < header_->numattachments; ++i, ++pattach) {
- aiNode *attachment_node = attachments_node->mChildren[i] = new aiNode();
- attachment_node->mParent = attachments_node;
- attachment_node->mMetaData = aiMetadata::Alloc(2);
- attachment_node->mMetaData->Set(0, "Position", aiVector3D(pattach->org[0], pattach->org[1], pattach->org[2]));
- // Reference the bone by name. This allows us to search a particular
- // bone by name using aiNode(s).
- attachment_node->mMetaData->Set(1, "Bone", temp_bones_[pattach->bone].node->mName);
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-void HL1MDLLoader::read_hitboxes() {
- if (!header_->numhitboxes) {
- return;
- }
-
- const Hitbox_HL1 *phitbox = (const Hitbox_HL1 *)((uint8_t *)header_ + header_->hitboxindex);
-
- aiNode *hitboxes_node = new aiNode(AI_MDL_HL1_NODE_HITBOXES);
- rootnode_children_.push_back(hitboxes_node);
- hitboxes_node->mNumChildren = static_cast<unsigned int>(header_->numhitboxes);
- hitboxes_node->mChildren = new aiNode *[hitboxes_node->mNumChildren];
-
- for (int i = 0; i < header_->numhitboxes; ++i, ++phitbox) {
- aiNode *hitbox_node = hitboxes_node->mChildren[i] = new aiNode();
- hitbox_node->mParent = hitboxes_node;
-
- aiMetadata *md = hitbox_node->mMetaData = aiMetadata::Alloc(4);
- // Reference the bone by name. This allows us to search a particular
- // bone by name using aiNode(s).
- md->Set(0, "Bone", temp_bones_[phitbox->bone].node->mName);
- md->Set(1, "HitGroup", phitbox->group);
- md->Set(2, "BBMin", aiVector3D(phitbox->bbmin[0], phitbox->bbmin[1], phitbox->bbmin[2]));
- md->Set(3, "BBMax", aiVector3D(phitbox->bbmax[0], phitbox->bbmax[1], phitbox->bbmax[2]));
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-void HL1MDLLoader::read_bone_controllers() {
- if (!header_->numbonecontrollers) {
- return;
- }
-
- const BoneController_HL1 *pbonecontroller = (const BoneController_HL1 *)((uint8_t *)header_ + header_->bonecontrollerindex);
-
- aiNode *bones_controller_node = new aiNode(AI_MDL_HL1_NODE_BONE_CONTROLLERS);
- rootnode_children_.push_back(bones_controller_node);
- bones_controller_node->mNumChildren = static_cast<unsigned int>(header_->numbonecontrollers);
- bones_controller_node->mChildren = new aiNode *[bones_controller_node->mNumChildren];
-
- for (int i = 0; i < header_->numbonecontrollers; ++i, ++pbonecontroller) {
- aiNode *bone_controller_node = bones_controller_node->mChildren[i] = new aiNode();
- bone_controller_node->mParent = bones_controller_node;
-
- aiMetadata *md = bone_controller_node->mMetaData = aiMetadata::Alloc(5);
- // Reference the bone by name. This allows us to search a particular
- // bone by name using aiNode(s).
- md->Set(0, "Bone", temp_bones_[pbonecontroller->bone].node->mName);
- md->Set(1, "MotionFlags", pbonecontroller->type);
- md->Set(2, "Start", pbonecontroller->start);
- md->Set(3, "End", pbonecontroller->end);
- md->Set(4, "Channel", pbonecontroller->index);
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-void HL1MDLLoader::read_global_info() {
- aiNode *global_info_node = new aiNode(AI_MDL_HL1_NODE_GLOBAL_INFO);
- rootnode_children_.push_back(global_info_node);
-
- aiMetadata *md = global_info_node->mMetaData = aiMetadata::Alloc(import_settings_.read_misc_global_info ? 16 : 11);
- md->Set(0, "Version", AI_MDL_HL1_VERSION);
- md->Set(1, "NumBodyparts", header_->numbodyparts);
- md->Set(2, "NumModels", total_models_);
- md->Set(3, "NumBones", header_->numbones);
- md->Set(4, "NumAttachments", import_settings_.read_attachments ? header_->numattachments : 0);
- md->Set(5, "NumSkinFamilies", texture_header_->numskinfamilies);
- md->Set(6, "NumHitboxes", import_settings_.read_hitboxes ? header_->numhitboxes : 0);
- md->Set(7, "NumBoneControllers", import_settings_.read_bone_controllers ? header_->numbonecontrollers : 0);
- md->Set(8, "NumSequences", import_settings_.read_animations ? header_->numseq : 0);
- md->Set(9, "NumBlendControllers", import_settings_.read_blend_controllers ? num_blend_controllers_ : 0);
- md->Set(10, "NumTransitionNodes", import_settings_.read_sequence_transitions ? header_->numtransitions : 0);
-
- if (import_settings_.read_misc_global_info) {
- md->Set(11, "EyePosition", aiVector3D(header_->eyeposition[0], header_->eyeposition[1], header_->eyeposition[2]));
- md->Set(12, "HullMin", aiVector3D(header_->min[0], header_->min[1], header_->min[2]));
- md->Set(13, "HullMax", aiVector3D(header_->max[0], header_->max[1], header_->max[2]));
- md->Set(14, "CollisionMin", aiVector3D(header_->bbmin[0], header_->bbmin[1], header_->bbmin[2]));
- md->Set(15, "CollisionMax", aiVector3D(header_->bbmax[0], header_->bbmax[1], header_->bbmax[2]));
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-/** @brief This method reads a compressed anim value.
-*
-* @note The structure of this method is taken from HL2 source code.
-* Although this is from HL2, it's implementation is almost identical
-* to code found in HL1 SDK. See HL1 and HL2 SDKs for more info.
-*
-* source:
-* HL1 source code.
-* file: studio_render.cpp
-* function(s): CalcBoneQuaternion and CalcBonePosition
-*
-* HL2 source code.
-* file: bone_setup.cpp
-* function(s): ExtractAnimValue
-*/
-void HL1MDLLoader::extract_anim_value(
- const AnimValue_HL1 *panimvalue,
- int frame, float bone_scale, ai_real &value) {
- int k = frame;
-
- // find span of values that includes the frame we want
- while (panimvalue->num.total <= k) {
- k -= panimvalue->num.total;
- panimvalue += panimvalue->num.valid + 1;
- }
-
- // Bah, missing blend!
- if (panimvalue->num.valid > k) {
- value = panimvalue[k + 1].value * bone_scale;
- } else {
- value = panimvalue[panimvalue->num.valid].value * bone_scale;
- }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get the number of blend controllers.
-bool HL1MDLLoader::get_num_blend_controllers(const int num_blend_animations, int &num_blend_controllers) {
-
- switch (num_blend_animations) {
- case SequenceBlendMode_HL1::NoBlend:
- num_blend_controllers = 0;
- return true;
- case SequenceBlendMode_HL1::TwoWayBlending:
- num_blend_controllers = 1;
- return true;
- case SequenceBlendMode_HL1::FourWayBlending:
- num_blend_controllers = 2;
- return true;
- default:
- num_blend_controllers = 0;
- ASSIMP_LOG_WARN(MDL_HALFLIFE_LOG_HEADER "Unsupported number of blend animations (", num_blend_animations, ")");
- return false;
- }
-}
-
-} // namespace HalfLife
-} // namespace MDL
-} // namespace Assimp