diff options
Diffstat (limited to 'libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp')
-rw-r--r-- | libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp | 1353 |
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 |