summaryrefslogtreecommitdiff
path: root/libs/assimp/code/AssetLib/MDL/HalfLife
diff options
context:
space:
mode:
Diffstat (limited to 'libs/assimp/code/AssetLib/MDL/HalfLife')
-rw-r--r--libs/assimp/code/AssetLib/MDL/HalfLife/HL1FileData.h600
-rw-r--r--libs/assimp/code/AssetLib/MDL/HalfLife/HL1ImportDefinitions.h64
-rw-r--r--libs/assimp/code/AssetLib/MDL/HalfLife/HL1ImportSettings.h85
-rw-r--r--libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp1353
-rw-r--r--libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.h243
-rw-r--r--libs/assimp/code/AssetLib/MDL/HalfLife/HL1MeshTrivert.h127
-rw-r--r--libs/assimp/code/AssetLib/MDL/HalfLife/HalfLifeMDLBaseHeader.h67
-rw-r--r--libs/assimp/code/AssetLib/MDL/HalfLife/LogFunctions.h95
-rw-r--r--libs/assimp/code/AssetLib/MDL/HalfLife/UniqueNameGenerator.cpp180
-rw-r--r--libs/assimp/code/AssetLib/MDL/HalfLife/UniqueNameGenerator.h81
10 files changed, 2895 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/MDL/HalfLife/HL1FileData.h b/libs/assimp/code/AssetLib/MDL/HalfLife/HL1FileData.h
new file mode 100644
index 0000000..28b1b28
--- /dev/null
+++ b/libs/assimp/code/AssetLib/MDL/HalfLife/HL1FileData.h
@@ -0,0 +1,600 @@
+/*
+---------------------------------------------------------------------------
+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 HL1FileData.h
+ * @brief Definition of in-memory structures for the
+ * Half-Life 1 MDL file format.
+ */
+
+#ifndef AI_HL1FILEDATA_INCLUDED
+#define AI_HL1FILEDATA_INCLUDED
+
+#include "HalfLifeMDLBaseHeader.h"
+
+#include <assimp/Compiler/pushpack1.h>
+#include <assimp/types.h>
+
+namespace Assimp {
+namespace MDL {
+namespace HalfLife {
+
+using vec3_t = float[3];
+
+/** \struct Header_HL1
+ * \brief Data structure for the HL1 MDL file header.
+ */
+struct Header_HL1 : HalfLifeMDLBaseHeader {
+ //! The model name.
+ char name[64];
+
+ //! The total file size in bytes.
+ int32_t length;
+
+ //! Ideal eye position.
+ vec3_t eyeposition;
+
+ //! Ideal movement hull size.
+ vec3_t min;
+ vec3_t max;
+
+ //! Clipping bounding box.
+ vec3_t bbmin;
+ vec3_t bbmax;
+
+ //! Was "flags".
+ int32_t unused;
+
+ //! The number of bones.
+ int32_t numbones;
+
+ //! Offset to the first bone chunk.
+ int32_t boneindex;
+
+ //! The number of bone controllers.
+ int32_t numbonecontrollers;
+
+ //! Offset to the first bone controller chunk.
+ int32_t bonecontrollerindex;
+
+ //! The number of hitboxes.
+ int32_t numhitboxes;
+
+ //! Offset to the first hitbox chunk.
+ int32_t hitboxindex;
+
+ //! The number of sequences.
+ int32_t numseq;
+
+ //! Offset to the first sequence description chunk.
+ int32_t seqindex;
+
+ //! The number of sequence groups.
+ int32_t numseqgroups;
+
+ //! Offset to the first sequence group chunk.
+ int32_t seqgroupindex;
+
+ //! The number of textures.
+ int32_t numtextures;
+
+ //! Offset to the first texture chunk.
+ int32_t textureindex;
+
+ //! Offset to the first texture's image data.
+ int32_t texturedataindex;
+
+ //! The number of replaceable textures.
+ int32_t numskinref;
+
+ //! The number of skin families.
+ int32_t numskinfamilies;
+
+ //! Offset to the first replaceable texture.
+ int32_t skinindex;
+
+ //! The number of bodyparts.
+ int32_t numbodyparts;
+
+ //! Offset the the first bodypart.
+ int32_t bodypartindex;
+
+ //! The number of attachments.
+ int32_t numattachments;
+
+ //! Offset the the first attachment chunk.
+ int32_t attachmentindex;
+
+ //! Was "soundtable".
+ int32_t unused2;
+
+ //! Was "soundindex".
+ int32_t unused3;
+
+ //! Was "soundgroups".
+ int32_t unused4;
+
+ //! Was "soundgroupindex".
+ int32_t unused5;
+
+ //! The number of nodes in the sequence transition graph.
+ int32_t numtransitions;
+
+ //! Offset the the first sequence transition.
+ int32_t transitionindex;
+} PACK_STRUCT;
+
+/** \struct SequenceHeader_HL1
+ * \brief Data structure for the file header of a demand loaded
+ * HL1 MDL sequence group file.
+ */
+struct SequenceHeader_HL1 : HalfLifeMDLBaseHeader {
+ //! The sequence group file name.
+ char name[64];
+
+ //! The total file size in bytes.
+ int32_t length;
+} PACK_STRUCT;
+
+/** \struct Bone_HL1
+ * \brief Data structure for a bone in HL1 MDL files.
+ */
+struct Bone_HL1 {
+ //! The bone name.
+ char name[32];
+
+ //! The parent bone index. (-1) If it has no parent.
+ int32_t parent;
+
+ //! Was "flags".
+ int32_t unused;
+
+ //! Available bone controller per motion type.
+ //! (-1) if no controller is available.
+ int32_t bonecontroller[6];
+
+ /*! Default position and rotation values where
+ * scale[0] = position.X
+ * scale[1] = position.Y
+ * scale[2] = position.Z
+ * scale[3] = rotation.X
+ * scale[4] = rotation.Y
+ * scale[5] = rotation.Z
+ */
+ float value[6];
+
+ /*! Compressed scale values where
+ * scale[0] = position.X scale
+ * scale[1] = position.Y scale
+ * scale[2] = position.Z scale
+ * scale[3] = rotation.X scale
+ * scale[4] = rotation.Y scale
+ * scale[5] = rotation.Z scale
+ */
+ float scale[6];
+} PACK_STRUCT;
+
+/** \struct BoneController_HL1
+ * \brief Data structure for a bone controller in HL1 MDL files.
+ */
+struct BoneController_HL1 {
+ //! Bone affected by this controller.
+ int32_t bone;
+
+ //! The motion type.
+ int32_t type;
+
+ //! The minimum and maximum values.
+ float start;
+ float end;
+
+ // Was "rest".
+ int32_t unused;
+
+ // The bone controller channel.
+ int32_t index;
+} PACK_STRUCT;
+
+/** \struct Hitbox_HL1
+ * \brief Data structure for a hitbox in HL1 MDL files.
+ */
+struct Hitbox_HL1 {
+ //! The bone this hitbox follows.
+ int32_t bone;
+
+ //! The hit group.
+ int32_t group;
+
+ //! The hitbox minimum and maximum extents.
+ vec3_t bbmin;
+ vec3_t bbmax;
+} PACK_STRUCT;
+
+/** \struct SequenceGroup_HL1
+ * \brief Data structure for a sequence group in HL1 MDL files.
+ */
+struct SequenceGroup_HL1 {
+ //! A textual name for this sequence group.
+ char label[32];
+
+ //! The file name.
+ char name[64];
+
+ //! Was "cache".
+ int32_t unused;
+
+ //! Was "data".
+ int32_t unused2;
+} PACK_STRUCT;
+
+//! The type of blending for a sequence.
+enum SequenceBlendMode_HL1 {
+ NoBlend = 1,
+ TwoWayBlending = 2,
+ FourWayBlending = 4,
+};
+
+/** \struct SequenceDesc_HL1
+ * \brief Data structure for a sequence description in HL1 MDL files.
+ */
+struct SequenceDesc_HL1 {
+ //! The sequence name.
+ char label[32];
+
+ //! Frames per second.
+ float fps;
+
+ //! looping/non-looping flags.
+ int32_t flags;
+
+ //! The sequence activity.
+ int32_t activity;
+
+ //! The sequence activity weight.
+ int32_t actweight;
+
+ //! The number of animation events.
+ int32_t numevents;
+
+ //! Offset the the first animation event chunk.
+ int32_t eventindex;
+
+ //! The number of frames in the sequence.
+ int32_t numframes;
+
+ //! Was "numpivots".
+ int32_t unused;
+
+ //! Was "pivotindex".
+ int32_t unused2;
+
+ //! Linear motion type.
+ int32_t motiontype;
+
+ //! Linear motion bone.
+ int32_t motionbone;
+
+ //! Linear motion.
+ vec3_t linearmovement;
+
+ //! Was "automoveposindex".
+ int32_t unused3;
+
+ //! Was "automoveangleindex".
+ int32_t unused4;
+
+ //! The sequence minimum and maximum extents.
+ vec3_t bbmin;
+ vec3_t bbmax;
+
+ //! The number of blend animations.
+ int32_t numblends;
+
+ //! Offset to first the AnimValueOffset_HL1 chunk.
+ //! This offset is relative to the SequenceHeader_HL1 of the file
+ //! that contains the animation data.
+ int32_t animindex;
+
+ //! The motion type of each blend controller.
+ int32_t blendtype[2];
+
+ //! The starting value of each blend controller.
+ float blendstart[2];
+
+ //! The ending value of each blend controller.
+ float blendend[2];
+
+ //! Was "blendparent".
+ int32_t unused5;
+
+ //! The sequence group.
+ int32_t seqgroup;
+
+ //! The node at entry in the sequence transition graph.
+ int32_t entrynode;
+
+ //! The node at exit in the sequence transition graph.
+ int32_t exitnode;
+
+ //! Transition rules.
+ int32_t nodeflags;
+
+ //! Was "nextseq"
+ int32_t unused6;
+} PACK_STRUCT;
+
+/** \struct AnimEvent_HL1
+ * \brief Data structure for an animation event in HL1 MDL files.
+ */
+struct AnimEvent_HL1 {
+ //! The frame at which this animation event occurs.
+ int32_t frame;
+
+ //! The script event type.
+ int32_t event;
+
+ //! was "type"
+ int32_t unused;
+
+ //! Options. Could be path to sound WAVE files.
+ char options[64];
+} PACK_STRUCT;
+
+/** \struct Attachment_HL1
+ * \brief Data structure for an attachment in HL1 MDL files.
+ */
+struct Attachment_HL1 {
+ //! Was "name".
+ char unused[32];
+
+ //! Was "type".
+ int32_t unused2;
+
+ //! The bone this attachment follows.
+ int32_t bone;
+
+ //! The attachment origin.
+ vec3_t org;
+
+ //! Was "vectors"
+ vec3_t unused3[3];
+} PACK_STRUCT;
+
+/** \struct AnimValueOffset_HL1
+ * \brief Data structure to hold offsets (one per motion type)
+ * to the first animation frame value for a single bone
+ * in HL1 MDL files.
+ */
+struct AnimValueOffset_HL1 {
+ unsigned short offset[6];
+} PACK_STRUCT;
+
+/** \struct AnimValue_HL1
+ * \brief Data structure for an animation frame in HL1 MDL files.
+ */
+union AnimValue_HL1 {
+ struct {
+ uint8_t valid;
+ uint8_t total;
+ } num;
+ short value;
+} PACK_STRUCT;
+
+/** \struct Bodypart_HL1
+ * \brief Data structure for a bodypart in HL1 MDL files.
+ */
+struct Bodypart_HL1 {
+ //! The bodypart name.
+ char name[64];
+
+ //! The number of available models for this bodypart.
+ int32_t nummodels;
+
+ //! Used to convert from a global model index
+ //! to a local bodypart model index.
+ int32_t base;
+
+ //! The offset to the first model chunk.
+ int32_t modelindex;
+} PACK_STRUCT;
+
+/** \struct Texture_HL1
+ * \brief Data structure for a texture in HL1 MDL files.
+ */
+struct Texture_HL1 {
+ //! Texture file name.
+ char name[64];
+
+ //! Texture flags.
+ int32_t flags;
+
+ //! Texture width in pixels.
+ int32_t width;
+
+ //! Texture height in pixels.
+ int32_t height;
+
+ //! Offset to the image data.
+ //! This offset is relative to the texture file header.
+ int32_t index;
+} PACK_STRUCT;
+
+/** \struct Model_HL1
+ * \brief Data structure for a model in HL1 MDL files.
+ */
+struct Model_HL1 {
+ //! Model name.
+ char name[64];
+
+ //! Was "type".
+ int32_t unused;
+
+ //! Was "boundingradius".
+ float unused2;
+
+ //! The number of meshes in the model.
+ int32_t nummesh;
+
+ //! Offset to the first mesh chunk.
+ int32_t meshindex;
+
+ //! The number of unique vertices.
+ int32_t numverts;
+
+ //! Offset to the vertex bone array.
+ int32_t vertinfoindex;
+
+ //! Offset to the vertex array.
+ int32_t vertindex;
+
+ //! The number of unique normals.
+ int32_t numnorms;
+
+ //! Offset to the normal bone array.
+ int32_t norminfoindex;
+
+ //! Offset to the normal array.
+ int32_t normindex;
+
+ //! Was "numgroups".
+ int32_t unused3;
+
+ //! Was "groupindex".
+ int32_t unused4;
+} PACK_STRUCT;
+
+/** \struct Mesh_HL1
+ * \brief Data structure for a mesh in HL1 MDL files.
+ */
+struct Mesh_HL1 {
+ //! Can be interpreted as the number of triangles in the mesh.
+ int32_t numtris;
+
+ //! Offset to the start of the tris sequence.
+ int32_t triindex;
+
+ //! The skin index.
+ int32_t skinref;
+
+ //! The number of normals in the mesh.
+ int32_t numnorms;
+
+ //! Was "normindex".
+ int32_t unused;
+} PACK_STRUCT;
+
+/** \struct Trivert
+ * \brief Data structure for a trivert in HL1 MDL files.
+ */
+struct Trivert {
+ //! Index into Model_HL1 vertex array.
+ short vertindex;
+
+ //! Index into Model_HL1 normal array.
+ short normindex;
+
+ //! Texture coordinates in absolute space (unnormalized).
+ short s, t;
+} PACK_STRUCT;
+
+#include <assimp/Compiler/poppack1.h>
+
+#if (!defined AI_MDL_HL1_VERSION)
+#define AI_MDL_HL1_VERSION 10
+#endif
+#if (!defined AI_MDL_HL1_MAX_TRIANGLES)
+#define AI_MDL_HL1_MAX_TRIANGLES 20000
+#endif
+#if (!defined AI_MDL_HL1_MAX_VERTICES)
+#define AI_MDL_HL1_MAX_VERTICES 2048
+#endif
+#if (!defined AI_MDL_HL1_MAX_SEQUENCES)
+#define AI_MDL_HL1_MAX_SEQUENCES 2048
+#endif
+#if (!defined AI_MDL_HL1_MAX_SEQUENCE_GROUPS)
+#define AI_MDL_HL1_MAX_SEQUENCE_GROUPS 32
+#endif
+#if (!defined AI_MDL_HL1_MAX_TEXTURES)
+#define AI_MDL_HL1_MAX_TEXTURES 100
+#endif
+#if (!defined AI_MDL_HL1_MAX_SKIN_FAMILIES)
+#define AI_MDL_HL1_MAX_SKIN_FAMILIES 100
+#endif
+#if (!defined AI_MDL_HL1_MAX_BONES)
+#define AI_MDL_HL1_MAX_BONES 128
+#endif
+#if (!defined AI_MDL_HL1_MAX_BODYPARTS)
+#define AI_MDL_HL1_MAX_BODYPARTS 32
+#endif
+#if (!defined AI_MDL_HL1_MAX_MODELS)
+#define AI_MDL_HL1_MAX_MODELS 32
+#endif
+#if (!defined AI_MDL_HL1_MAX_MESHES)
+#define AI_MDL_HL1_MAX_MESHES 256
+#endif
+#if (!defined AI_MDL_HL1_MAX_EVENTS)
+#define AI_MDL_HL1_MAX_EVENTS 1024
+#endif
+#if (!defined AI_MDL_HL1_MAX_BONE_CONTROLLERS)
+#define AI_MDL_HL1_MAX_BONE_CONTROLLERS 8
+#endif
+#if (!defined AI_MDL_HL1_MAX_ATTACHMENTS)
+#define AI_MDL_HL1_MAX_ATTACHMENTS 512
+#endif
+
+// lighting options
+#if (!defined AI_MDL_HL1_STUDIO_NF_FLATSHADE)
+#define AI_MDL_HL1_STUDIO_NF_FLATSHADE 0x0001
+#endif
+#if (!defined AI_MDL_HL1_STUDIO_NF_CHROME)
+#define AI_MDL_HL1_STUDIO_NF_CHROME 0x0002
+#endif
+#if (!defined AI_MDL_HL1_STUDIO_NF_ADDITIVE)
+#define AI_MDL_HL1_STUDIO_NF_ADDITIVE 0x0020
+#endif
+#if (!defined AI_MDL_HL1_STUDIO_NF_MASKED)
+#define AI_MDL_HL1_STUDIO_NF_MASKED 0x0040
+#endif
+
+} // namespace HalfLife
+} // namespace MDL
+} // namespace Assimp
+
+#endif // AI_HL1FILEDATA_INCLUDED
diff --git a/libs/assimp/code/AssetLib/MDL/HalfLife/HL1ImportDefinitions.h b/libs/assimp/code/AssetLib/MDL/HalfLife/HL1ImportDefinitions.h
new file mode 100644
index 0000000..d412aee
--- /dev/null
+++ b/libs/assimp/code/AssetLib/MDL/HalfLife/HL1ImportDefinitions.h
@@ -0,0 +1,64 @@
+/*
+---------------------------------------------------------------------------
+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 HL1ImportDefinitions.h
+ * @brief HL1 MDL loader specific definitions.
+ */
+
+#ifndef AI_MDL_HL1_IMPORT_DEFINITIONS_INCLUDED
+#define AI_MDL_HL1_IMPORT_DEFINITIONS_INCLUDED
+
+#define AI_MDL_HL1_NODE_ROOT "<MDL_root>"
+#define AI_MDL_HL1_NODE_BODYPARTS "<MDL_bodyparts>"
+#define AI_MDL_HL1_NODE_BONES "<MDL_bones>"
+#define AI_MDL_HL1_NODE_BONE_CONTROLLERS "<MDL_bone_controllers>"
+#define AI_MDL_HL1_NODE_SEQUENCE_INFOS "<MDL_sequence_infos>"
+#define AI_MDL_HL1_NODE_SEQUENCE_GROUPS "<MDL_sequence_groups>"
+#define AI_MDL_HL1_NODE_SEQUENCE_TRANSITION_GRAPH "<MDL_sequence_transition_graph>"
+#define AI_MDL_HL1_NODE_ATTACHMENTS "<MDL_attachments>"
+#define AI_MDL_HL1_NODE_HITBOXES "<MDL_hitboxes>"
+#define AI_MDL_HL1_NODE_GLOBAL_INFO "<MDL_global_info>"
+#define AI_MDL_HL1_NODE_ANIMATION_EVENTS "AnimationEvents"
+#define AI_MDL_HL1_NODE_BLEND_CONTROLLERS "BlendControllers"
+
+#define AI_MDL_HL1_MATKEY_CHROME(type, N) "$mat.HL1.chrome", type, N
+
+#endif // AI_MDL_HL1_IMPORT_DEFINITIONS_INCLUDED
diff --git a/libs/assimp/code/AssetLib/MDL/HalfLife/HL1ImportSettings.h b/libs/assimp/code/AssetLib/MDL/HalfLife/HL1ImportSettings.h
new file mode 100644
index 0000000..340ba2d
--- /dev/null
+++ b/libs/assimp/code/AssetLib/MDL/HalfLife/HL1ImportSettings.h
@@ -0,0 +1,85 @@
+/*
+---------------------------------------------------------------------------
+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 HL1ImportSettings.h
+ * @brief Half-Life 1 MDL loader configuration settings.
+ */
+
+#ifndef AI_HL1IMPORTSETTINGS_INCLUDED
+#define AI_HL1IMPORTSETTINGS_INCLUDED
+
+#include <string>
+
+namespace Assimp {
+namespace MDL {
+namespace HalfLife {
+
+struct HL1ImportSettings {
+ HL1ImportSettings() :
+ read_animations(false),
+ read_animation_events(false),
+ read_blend_controllers(false),
+ read_sequence_groups_info(false),
+ read_sequence_transitions(false),
+ read_attachments(false),
+ read_bone_controllers(false),
+ read_hitboxes(false),
+ read_textures(false),
+ read_misc_global_info(false) {
+ }
+
+ bool read_animations;
+ bool read_animation_events;
+ bool read_blend_controllers;
+ bool read_sequence_groups_info;
+ bool read_sequence_transitions;
+ bool read_attachments;
+ bool read_bone_controllers;
+ bool read_hitboxes;
+ bool read_textures;
+ bool read_misc_global_info;
+};
+
+} // namespace HalfLife
+} // namespace MDL
+} // namespace Assimp
+
+#endif // AI_HL1IMPORTSETTINGS_INCLUDED
diff --git a/libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp b/libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp
new file mode 100644
index 0000000..93d3753
--- /dev/null
+++ b/libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp
@@ -0,0 +1,1353 @@
+/*
+---------------------------------------------------------------------------
+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
diff --git a/libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.h b/libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.h
new file mode 100644
index 0000000..d87d6d3
--- /dev/null
+++ b/libs/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.h
@@ -0,0 +1,243 @@
+/*
+---------------------------------------------------------------------------
+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.h
+ * @brief Declaration of the Half-Life 1 MDL loader.
+ */
+
+#ifndef AI_HL1MDLLOADER_INCLUDED
+#define AI_HL1MDLLOADER_INCLUDED
+
+#include "HL1FileData.h"
+#include "HL1ImportSettings.h"
+#include "UniqueNameGenerator.h"
+
+#include <memory>
+#include <string>
+
+#include <assimp/types.h>
+#include <assimp/scene.h>
+#include <assimp/texture.h>
+#include <assimp/IOSystem.hpp>
+#include <assimp/DefaultIOSystem.h>
+#include <assimp/Exceptional.h>
+
+namespace Assimp {
+namespace MDL {
+namespace HalfLife {
+
+class HL1MDLLoader {
+public:
+ HL1MDLLoader() = delete;
+ HL1MDLLoader(const HL1MDLLoader &) = delete;
+
+ /** See variables descriptions at the end for more details. */
+ HL1MDLLoader(
+ aiScene *scene,
+ IOSystem *io,
+ const unsigned char *buffer,
+ const std::string &file_path,
+ const HL1ImportSettings &import_settings);
+
+ ~HL1MDLLoader();
+
+ void load_file();
+
+protected:
+ /** \brief Validate the header data structure of a Half-Life 1 MDL file.
+ * \param[in] header Input header to be validated.
+ * \param[in] is_texture_header Whether or not we are reading an MDL
+ * texture file.
+ */
+ void validate_header(const Header_HL1 *header, bool is_texture_header);
+
+ void load_texture_file();
+ void load_sequence_groups_files();
+ void read_textures();
+ void read_skins();
+ void read_bones();
+ void read_meshes();
+ void read_animations();
+ void read_sequence_groups_info();
+ void read_sequence_infos();
+ void read_sequence_transitions();
+ void read_attachments();
+ void read_hitboxes();
+ void read_bone_controllers();
+ void read_global_info();
+
+private:
+ void release_resources();
+
+ /** \brief Load a file and copy it's content to a buffer.
+ * \param file_path The path to the file to be loaded.
+ * \param buffer A pointer to a buffer to receive the data.
+ */
+ template <typename MDLFileHeader>
+ void load_file_into_buffer(const std::string &file_path, unsigned char *&buffer);
+
+ /** \brief Read an MDL texture.
+ * \param[in] ptexture A pointer to an MDL texture.
+ * \param[in] data A pointer to the data from \p ptexture.
+ * \param[in] pal A pointer to the texture palette from \p ptexture.
+ * \param[in,out] pResult A pointer to the output resulting Assimp texture.
+ * \param[in,out] last_palette_color The last color from the image palette.
+ */
+ void read_texture(const Texture_HL1 *ptexture,
+ uint8_t *data, uint8_t *pal, aiTexture *pResult,
+ aiColor3D &last_palette_color);
+
+ /** \brief This method reads a compressed anim value.
+ * \param[in] panimvalue A pointer to the animation data.
+ * \param[in] frame The frame to look for.
+ * \param[in] bone_scale The current bone scale to apply to the compressed value.
+ * \param[in,out] value The decompressed anim value at \p frame.
+ */
+ void extract_anim_value(const AnimValue_HL1 *panimvalue,
+ int frame, float bone_scale, ai_real &value);
+
+ /**
+ * \brief Given the number of blend animations, determine the number of blend controllers.
+ *
+ * \param[in] num_blend_animations The number of blend animations.
+ * \param[out] num_blend_controllers The number of blend controllers.
+ * \return True if the number of blend controllers was determined. False otherwise.
+ */
+ static bool get_num_blend_controllers(const int num_blend_animations, int &num_blend_controllers);
+
+ /** Output scene to be filled */
+ aiScene *scene_;
+
+ /** Output I/O handler. Required for additional IO operations. */
+ IOSystem *io_;
+
+ /** Buffer from MDLLoader class. */
+ const unsigned char *buffer_;
+
+ /** The full file path to the MDL file we are trying to load.
+ * Used to locate other MDL files since MDL may store resources
+ * in external MDL files. */
+ const std::string &file_path_;
+
+ /** Configuration for HL1 MDL */
+ const HL1ImportSettings &import_settings_;
+
+ /** Main MDL header. */
+ const Header_HL1 *header_;
+
+ /** External MDL texture header. */
+ const Header_HL1 *texture_header_;
+
+ /** External MDL animation headers.
+ * One for each loaded animation file. */
+ SequenceHeader_HL1 **anim_headers_;
+
+ /** Texture file data. */
+ unsigned char *texture_buffer_;
+
+ /** Animation files data. */
+ unsigned char **anim_buffers_;
+
+ /** The number of sequence groups. */
+ int num_sequence_groups_;
+
+ /** The list of children to be appended to the scene's root node. */
+ std::vector<aiNode *> rootnode_children_;
+
+ /** A unique name generator. Used to generate names for MDL values
+ * that may have empty/duplicate names. */
+ UniqueNameGenerator unique_name_generator_;
+
+ /** The list of unique sequence names. */
+ std::vector<std::string> unique_sequence_names_;
+
+ /** The list of unique sequence groups names. */
+ std::vector<std::string> unique_sequence_groups_names_;
+
+ /** Structure to store temporary bone information. */
+ struct TempBone {
+
+ TempBone() :
+ node(nullptr),
+ absolute_transform(),
+ offset_matrix() {}
+
+ aiNode *node;
+ aiMatrix4x4 absolute_transform;
+ aiMatrix4x4 offset_matrix;
+ };
+
+ std::vector<TempBone> temp_bones_;
+
+ /** The number of available bone controllers in the model. */
+ int num_blend_controllers_;
+
+ /** Self explanatory. */
+ int total_models_;
+};
+
+// ------------------------------------------------------------------------------------------------
+template <typename MDLFileHeader>
+void HL1MDLLoader::load_file_into_buffer(const std::string &file_path, unsigned char *&buffer) {
+ if (!io_->Exists(file_path))
+ throw DeadlyImportError("Missing file ", DefaultIOSystem::fileName(file_path), ".");
+
+ std::unique_ptr<IOStream> file(io_->Open(file_path));
+
+ if (file.get() == nullptr) {
+ throw DeadlyImportError("Failed to open MDL file ", DefaultIOSystem::fileName(file_path), ".");
+ }
+
+ const size_t file_size = file->FileSize();
+ if (file_size < sizeof(MDLFileHeader)) {
+ throw DeadlyImportError("MDL file is too small.");
+ }
+
+ buffer = new unsigned char[1 + file_size];
+ file->Read((void *)buffer, 1, file_size);
+ buffer[file_size] = '\0';
+}
+
+} // namespace HalfLife
+} // namespace MDL
+} // namespace Assimp
+
+#endif // AI_HL1MDLLOADER_INCLUDED
diff --git a/libs/assimp/code/AssetLib/MDL/HalfLife/HL1MeshTrivert.h b/libs/assimp/code/AssetLib/MDL/HalfLife/HL1MeshTrivert.h
new file mode 100644
index 0000000..4ef8a13
--- /dev/null
+++ b/libs/assimp/code/AssetLib/MDL/HalfLife/HL1MeshTrivert.h
@@ -0,0 +1,127 @@
+/*
+---------------------------------------------------------------------------
+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 HL1MeshTrivert.h
+ * @brief This file contains the class declaration for the
+ * HL1 mesh trivert class.
+ */
+
+#ifndef AI_HL1MESHTRIVERT_INCLUDED
+#define AI_HL1MESHTRIVERT_INCLUDED
+
+#include "HL1FileData.h"
+
+namespace Assimp {
+namespace MDL {
+namespace HalfLife {
+
+/* A class to help map model triverts to mesh triverts. */
+struct HL1MeshTrivert {
+ HL1MeshTrivert() :
+ vertindex(-1),
+ normindex(-1),
+ s(0),
+ t(0),
+ localindex(-1) {
+ }
+
+ HL1MeshTrivert(short vertindex, short normindex, short s, short t, short localindex) :
+ vertindex(vertindex),
+ normindex(normindex),
+ s(s),
+ t(t),
+ localindex(localindex) {
+ }
+
+ HL1MeshTrivert(const Trivert &a) :
+ vertindex(a.vertindex),
+ normindex(a.normindex),
+ s(a.s),
+ t(a.t),
+ localindex(-1) {
+ }
+
+ inline bool operator==(const Trivert &a) const {
+ return vertindex == a.vertindex &&
+ normindex == a.normindex &&
+ s == a.s &&
+ t == a.t;
+ }
+
+ inline bool operator!=(const Trivert &a) const {
+ return !(*this == a);
+ }
+
+ inline bool operator==(const HL1MeshTrivert &a) const {
+ return localindex == a.localindex &&
+ vertindex == a.vertindex &&
+ normindex == a.normindex &&
+ s == a.s &&
+ t == a.t;
+ }
+
+ inline bool operator!=(const HL1MeshTrivert &a) const {
+ return !(*this == a);
+ }
+
+ inline HL1MeshTrivert &operator=(const Trivert &other) {
+ vertindex = other.vertindex;
+ normindex = other.normindex;
+ s = other.s;
+ t = other.t;
+ return *this;
+ }
+
+ short vertindex;
+ short normindex;
+ short s, t;
+ short localindex;
+};
+
+struct HL1MeshFace {
+ short v0, v1, v2;
+};
+
+} // namespace HalfLife
+} // namespace MDL
+} // namespace Assimp
+
+#endif // AI_HL1MESHTRIVERT_INCLUDED
diff --git a/libs/assimp/code/AssetLib/MDL/HalfLife/HalfLifeMDLBaseHeader.h b/libs/assimp/code/AssetLib/MDL/HalfLife/HalfLifeMDLBaseHeader.h
new file mode 100644
index 0000000..c7808c4
--- /dev/null
+++ b/libs/assimp/code/AssetLib/MDL/HalfLife/HalfLifeMDLBaseHeader.h
@@ -0,0 +1,67 @@
+/*
+---------------------------------------------------------------------------
+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 HalfLifeMDLBaseHeader.h */
+
+#ifndef AI_HALFLIFEMDLBASEHEADER_INCLUDED
+#define AI_HALFLIFEMDLBASEHEADER_INCLUDED
+
+#include <assimp/types.h>
+
+namespace Assimp {
+namespace MDL {
+namespace HalfLife {
+
+/** Used to interface different Valve MDL formats. */
+struct HalfLifeMDLBaseHeader
+{
+ //! Magic number: "IDST"/"IDSQ"
+ char ident[4];
+
+ //! The file format version.
+ int32_t version;
+};
+
+}
+}
+}
+
+#endif // AI_HALFLIFEMDLBASEHEADER_INCLUDED
diff --git a/libs/assimp/code/AssetLib/MDL/HalfLife/LogFunctions.h b/libs/assimp/code/AssetLib/MDL/HalfLife/LogFunctions.h
new file mode 100644
index 0000000..003774d
--- /dev/null
+++ b/libs/assimp/code/AssetLib/MDL/HalfLife/LogFunctions.h
@@ -0,0 +1,95 @@
+/*
+---------------------------------------------------------------------------
+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 LogFunctions.h */
+
+#ifndef AI_MDL_HALFLIFE_LOGFUNCTIONS_INCLUDED
+#define AI_MDL_HALFLIFE_LOGFUNCTIONS_INCLUDED
+
+#include <assimp/Logger.hpp>
+#include <string>
+
+namespace Assimp {
+namespace MDL {
+namespace HalfLife {
+
+/**
+ * \brief A function to log precise messages regarding limits exceeded.
+ *
+ * \param[in] subject Subject.
+ * \param[in] current_amount Current amount.
+ * \param[in] direct_object Direct object.
+ * LIMIT Limit constant.
+ *
+ * Example: Model has 100 textures, which exceeds the limit (50)
+ *
+ * where \p subject is 'Model'
+ * \p current_amount is '100'
+ * \p direct_object is 'textures'
+ * LIMIT is '50'
+ */
+template <int LIMIT>
+static inline void log_warning_limit_exceeded(
+ const std::string &subject, int current_amount,
+ const std::string &direct_object) {
+
+ ASSIMP_LOG_WARN(MDL_HALFLIFE_LOG_HEADER
+ + subject
+ + " has "
+ + std::to_string(current_amount) + " "
+ + direct_object
+ + ", which exceeds the limit ("
+ + std::to_string(LIMIT)
+ + ")");
+}
+
+/** \brief Same as above, but uses 'Model' as the subject. */
+template <int LIMIT>
+static inline void log_warning_limit_exceeded(int current_amount,
+ const std::string &direct_object) {
+ log_warning_limit_exceeded<LIMIT>("Model", current_amount, direct_object);
+}
+
+} // namespace HalfLife
+} // namespace MDL
+} // namespace Assimp
+
+#endif // AI_MDL_HALFLIFE_LOGFUNCTIONS_INCLUDED
diff --git a/libs/assimp/code/AssetLib/MDL/HalfLife/UniqueNameGenerator.cpp b/libs/assimp/code/AssetLib/MDL/HalfLife/UniqueNameGenerator.cpp
new file mode 100644
index 0000000..6fc8b11
--- /dev/null
+++ b/libs/assimp/code/AssetLib/MDL/HalfLife/UniqueNameGenerator.cpp
@@ -0,0 +1,180 @@
+/*
+---------------------------------------------------------------------------
+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 UniqueNameGenerator.cpp
+ * @brief Implementation for the unique name generator.
+ */
+
+#include "UniqueNameGenerator.h"
+#include <algorithm>
+#include <list>
+#include <map>
+#include <numeric>
+
+namespace Assimp {
+namespace MDL {
+namespace HalfLife {
+
+UniqueNameGenerator::UniqueNameGenerator() :
+ template_name_("unnamed"),
+ separator_("_") {
+}
+
+UniqueNameGenerator::UniqueNameGenerator(const char *template_name) :
+ template_name_(template_name),
+ separator_("_") {
+}
+
+UniqueNameGenerator::UniqueNameGenerator(const char *template_name, const char *separator) :
+ template_name_(template_name),
+ separator_(separator) {
+}
+
+UniqueNameGenerator::~UniqueNameGenerator() {
+}
+
+void UniqueNameGenerator::make_unique(std::vector<std::string> &names) {
+ struct DuplicateInfo {
+ DuplicateInfo() :
+ indices(),
+ next_id(0) {
+ }
+
+ std::list<size_t> indices;
+ size_t next_id;
+ };
+
+ std::vector<size_t> empty_names_indices;
+ std::vector<size_t> template_name_duplicates;
+ std::map<std::string, DuplicateInfo> names_to_duplicates;
+
+ const std::string template_name_with_separator(template_name_ + separator_);
+
+ auto format_name = [&](const std::string &base_name, size_t id) -> std::string {
+ return base_name + separator_ + std::to_string(id);
+ };
+
+ auto generate_unique_name = [&](const std::string &base_name) -> std::string {
+ auto *duplicate_info = &names_to_duplicates[base_name];
+
+ std::string new_name;
+
+ bool found_identical_name;
+ bool tried_with_base_name_only = false;
+ do {
+ // Assume that no identical name exists.
+ found_identical_name = false;
+
+ if (!tried_with_base_name_only) {
+ // First try with only the base name.
+ new_name = base_name;
+ } else {
+ // Create the name expected to be unique.
+ new_name = format_name(base_name, duplicate_info->next_id);
+ }
+
+ // Check in the list of duplicates for an identical name.
+ for (size_t i = 0;
+ i < names.size() &&
+ !found_identical_name;
+ ++i) {
+ if (new_name == names[i])
+ found_identical_name = true;
+ }
+
+ if (tried_with_base_name_only)
+ ++duplicate_info->next_id;
+
+ tried_with_base_name_only = true;
+
+ } while (found_identical_name);
+
+ return new_name;
+ };
+
+ for (size_t i = 0; i < names.size(); ++i) {
+ // Check for empty names.
+ if (names[i].find_first_not_of(' ') == std::string::npos) {
+ empty_names_indices.push_back(i);
+ continue;
+ }
+
+ /* Check for potential duplicate.
+ a) Either if this name is the same as the template name or
+ b) <template name><separator> is found at the beginning. */
+ if (names[i] == template_name_ ||
+ names[i].substr(0, template_name_with_separator.length()) == template_name_with_separator)
+ template_name_duplicates.push_back(i);
+
+ // Map each unique name to it's duplicate.
+ if (names_to_duplicates.count(names[i]) == 0)
+ names_to_duplicates.insert({ names[i], DuplicateInfo()});
+ else
+ names_to_duplicates[names[i]].indices.push_back(i);
+ }
+
+ // Make every non-empty name unique.
+ for (auto it = names_to_duplicates.begin();
+ it != names_to_duplicates.end(); ++it) {
+ for (auto it2 = it->second.indices.begin();
+ it2 != it->second.indices.end();
+ ++it2)
+ names[*it2] = generate_unique_name(it->first);
+ }
+
+ // Generate a unique name for every empty string.
+ if (template_name_duplicates.size()) {
+ // At least one string ressembles to <template name>.
+ for (auto it = empty_names_indices.begin();
+ it != empty_names_indices.end(); ++it)
+ names[*it] = generate_unique_name(template_name_);
+ } else {
+ // No string alike <template name> exists.
+ size_t i = 0;
+ for (auto it = empty_names_indices.begin();
+ it != empty_names_indices.end(); ++it, ++i)
+ names[*it] = format_name(template_name_, i);
+ }
+}
+
+} // namespace HalfLife
+} // namespace MDL
+} // namespace Assimp
diff --git a/libs/assimp/code/AssetLib/MDL/HalfLife/UniqueNameGenerator.h b/libs/assimp/code/AssetLib/MDL/HalfLife/UniqueNameGenerator.h
new file mode 100644
index 0000000..73b6f9e
--- /dev/null
+++ b/libs/assimp/code/AssetLib/MDL/HalfLife/UniqueNameGenerator.h
@@ -0,0 +1,81 @@
+/*
+---------------------------------------------------------------------------
+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 UniqueNameGenerator.h
+ * @brief Declaration of the unique name generator.
+ */
+
+#ifndef AI_UNIQUENAMEGENERATOR_INCLUDED
+#define AI_UNIQUENAMEGENERATOR_INCLUDED
+
+#include <string>
+#include <vector>
+
+namespace Assimp {
+namespace MDL {
+namespace HalfLife {
+
+class UniqueNameGenerator {
+public:
+ UniqueNameGenerator();
+ UniqueNameGenerator(const char *template_name);
+ UniqueNameGenerator(const char *template_name, const char *separator);
+ ~UniqueNameGenerator();
+
+ inline void set_template_name(const char *template_name) {
+ template_name_ = template_name;
+ }
+ inline void set_separator(const char *separator) {
+ separator_ = separator;
+ }
+
+ void make_unique(std::vector<std::string> &names);
+
+private:
+ std::string template_name_;
+ std::string separator_;
+};
+
+} // namespace HalfLife
+} // namespace MDL
+} // namespace Assimp
+
+#endif // AI_UNIQUENAMEGENERATOR_INCLUDED