From 97dc0ce15d038feb47a2f054cb53745c869e07c8 Mon Sep 17 00:00:00 2001 From: Dylan Kenneally Date: Thu, 21 Nov 2019 12:04:53 +1100 Subject: [PATCH 01/36] Added CMake option to set the compiler warning to max (-Wall /W4). Off by default --- Build.md | 1 + CMakeLists.txt | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/Build.md b/Build.md index 2db47798d..4b7513313 100644 --- a/Build.md +++ b/Build.md @@ -77,6 +77,7 @@ The cmake-build-environment provides options to configure the build. The followi - **ASSIMP_BUILD_SAMPLES ( default OFF )**: If the official samples are built as well (needs Glut). - **ASSIMP_BUILD_TESTS ( default ON )**: If the test suite for Assimp is built in addition to the library. - **ASSIMP_COVERALLS ( default OFF )**: Enable this to measure test coverage. +- **ASSIMP_ERROR_MAX( default OFF)**: Enable all warnings. - **ASSIMP_WERROR( default OFF )**: Treat warnings as errors. - **ASSIMP_ASAN ( default OFF )**: Enable AddressSanitizer. - **ASSIMP_UBSAN ( default OFF )**: Enable Undefined Behavior sanitizer. diff --git a/CMakeLists.txt b/CMakeLists.txt index 693d6f16a..f69d2c9a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,10 @@ OPTION ( ASSIMP_COVERALLS "Enable this to measure test coverage." OFF ) +OPTION ( ASSIMP_ERROR_MAX + "Enable all warnings." + OFF +) OPTION ( ASSIMP_WERROR "Treat warnings as errors." OFF @@ -294,6 +298,16 @@ IF (ASSIMP_COVERALLS) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") ENDIF() +IF (ASSIMP_ERROR_MAX) + MESSAGE(STATUS "Turning on all warnings") + IF (MSVC) + ADD_COMPILE_OPTIONS(/W4) # NB: there is a /Wall option, pedantic mode + ELSE() + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") + ENDIF() +ENDIF() + IF (ASSIMP_WERROR) MESSAGE(STATUS "Treating warnings as errors") IF (MSVC) From 71cac7ab8dca879958bc09b9efb62510feb2de3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Sat, 7 Dec 2019 22:04:04 +0100 Subject: [PATCH 02/36] lower-case PEP263 encoding declaration so emacs likes it... --- port/PyAssimp/pyassimp/structs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/PyAssimp/pyassimp/structs.py b/port/PyAssimp/pyassimp/structs.py index d478b861b..bd90586ff 100644 --- a/port/PyAssimp/pyassimp/structs.py +++ b/port/PyAssimp/pyassimp/structs.py @@ -1,4 +1,4 @@ -#-*- coding: UTF-8 -*- +#-*- coding: utf-8 -*- from ctypes import POINTER, c_void_p, c_uint, c_char, c_float, Structure, c_char_p, c_double, c_ubyte, c_size_t, c_uint32 From 298a89b4eedcd8b18a178bc399f3262220122841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Sat, 7 Dec 2019 22:06:14 +0100 Subject: [PATCH 03/36] remove trailing whitespace --- port/PyAssimp/pyassimp/structs.py | 264 +++++++++++++++--------------- 1 file changed, 132 insertions(+), 132 deletions(-) diff --git a/port/PyAssimp/pyassimp/structs.py b/port/PyAssimp/pyassimp/structs.py index bd90586ff..b93aae68c 100644 --- a/port/PyAssimp/pyassimp/structs.py +++ b/port/PyAssimp/pyassimp/structs.py @@ -6,7 +6,7 @@ from ctypes import POINTER, c_void_p, c_uint, c_char, c_float, Structure, c_char class Vector2D(Structure): """ See 'vector2.h' for details. - """ + """ _fields_ = [ @@ -16,7 +16,7 @@ class Vector2D(Structure): class Matrix3x3(Structure): """ See 'matrix3x3.h' for details. - """ + """ _fields_ = [ @@ -28,7 +28,7 @@ class Matrix3x3(Structure): class Texel(Structure): """ See 'texture.h' for details. - """ + """ _fields_ = [ ("b", c_ubyte),("g", c_ubyte),("r", c_ubyte),("a", c_ubyte), @@ -37,7 +37,7 @@ class Texel(Structure): class Color4D(Structure): """ See 'color4.h' for details. - """ + """ _fields_ = [ @@ -48,7 +48,7 @@ class Color4D(Structure): class Plane(Structure): """ See 'types.h' for details. - """ + """ _fields_ = [ # Plane equation @@ -58,7 +58,7 @@ class Plane(Structure): class Color3D(Structure): """ See 'types.h' for details. - """ + """ _fields_ = [ # Red, green and blue color values @@ -68,7 +68,7 @@ class Color3D(Structure): class String(Structure): """ See 'types.h' for details. - """ + """ MAXLEN = 1024 @@ -77,7 +77,7 @@ class String(Structure): # logical length of strings containing UTF-8 multibyte sequences! It's # the number of bytes from the beginning of the string to its end. ("length", c_size_t), - + # String buffer. Size limit is MAXLEN ("data", c_char*MAXLEN), ] @@ -85,7 +85,7 @@ class String(Structure): class MaterialPropertyString(Structure): """ See 'MaterialSystem.cpp' for details. - + The size of length is truncated to 4 bytes on 64-bit platforms when used as a material property (see MaterialSystem.cpp aiMaterial::AddProperty() for details). """ @@ -97,7 +97,7 @@ class MaterialPropertyString(Structure): # logical length of strings containing UTF-8 multibyte sequences! It's # the number of bytes from the beginning of the string to its end. ("length", c_uint32), - + # String buffer. Size limit is MAXLEN ("data", c_char*MAXLEN), ] @@ -105,30 +105,30 @@ class MaterialPropertyString(Structure): class MemoryInfo(Structure): """ See 'types.h' for details. - """ + """ _fields_ = [ # Storage allocated for texture data ("textures", c_uint), - + # Storage allocated for material data ("materials", c_uint), - + # Storage allocated for mesh data ("meshes", c_uint), - + # Storage allocated for node data ("nodes", c_uint), - + # Storage allocated for animation data ("animations", c_uint), - + # Storage allocated for camera data ("cameras", c_uint), - + # Storage allocated for light data ("lights", c_uint), - + # Total storage allocated for the full import. ("total", c_uint), ] @@ -136,7 +136,7 @@ class MemoryInfo(Structure): class Quaternion(Structure): """ See 'quaternion.h' for details. - """ + """ _fields_ = [ @@ -147,14 +147,14 @@ class Quaternion(Structure): class Face(Structure): """ See 'mesh.h' for details. - """ + """ _fields_ = [ # Number of indices defining this face. # The maximum value for this member is #AI_MAX_FACE_INDICES. ("mNumIndices", c_uint), - + # Pointer to the indices array. Size of the array is given in numIndices. ("mIndices", POINTER(c_uint)), ] @@ -162,12 +162,12 @@ class Face(Structure): class VertexWeight(Structure): """ See 'mesh.h' for details. - """ + """ _fields_ = [ # Index of the vertex which is influenced by the bone. ("mVertexId", c_uint), - + # The strength of the influence in the range (0...1). # The influence from all bones at one vertex amounts to 1. ("mWeight", c_float), @@ -176,7 +176,7 @@ class VertexWeight(Structure): class Matrix4x4(Structure): """ See 'matrix4x4.h' for details. - """ + """ _fields_ = [ @@ -189,7 +189,7 @@ class Matrix4x4(Structure): class Vector3D(Structure): """ See 'vector3.h' for details. - """ + """ _fields_ = [ @@ -199,12 +199,12 @@ class Vector3D(Structure): class MeshKey(Structure): """ See 'anim.h' for details. - """ + """ _fields_ = [ # The time of this key ("mTime", c_double), - + # Index into the aiMesh::mAnimMeshes array of the # mesh corresponding to the #aiMeshAnim hosting this @@ -252,7 +252,7 @@ class Metadata(Structure): class Node(Structure): """ See 'scene.h' for details. - """ + """ Node._fields_ = [ @@ -270,22 +270,22 @@ Node._fields_ = [ # this text. You should be able to handle stuff like whitespace, tabs, # linefeeds, quotation marks, ampersands, ... . ("mName", String), - + # The transformation relative to the node's parent. ("mTransformation", Matrix4x4), - + # Parent node. NULL if this node is the root node. ("mParent", POINTER(Node)), - + # The number of child nodes of this node. ("mNumChildren", c_uint), - + # The child nodes of this node. NULL if mNumChildren is 0. ("mChildren", POINTER(POINTER(Node))), - + # The number of meshes of this node. ("mNumMeshes", c_uint), - + # The meshes of this node. Each entry is an index into the mesh ("mMeshes", POINTER(c_uint)), @@ -297,7 +297,7 @@ Node._fields_ = [ class Light(Structure): """ See 'light.h' for details. - """ + """ _fields_ = [ @@ -306,22 +306,22 @@ class Light(Structure): # This node specifies the position of the light in the scene # hierarchy and can be animated. ("mName", String), - + # The type of the light source. # aiLightSource_UNDEFINED is not a valid value for this member. ("mType", c_uint), - + # Position of the light source in space. Relative to the # transformation of the node corresponding to the light. # The position is undefined for directional lights. ("mPosition", Vector3D), - + # Direction of the light source in space. Relative to the # transformation of the node corresponding to the light. # The direction is undefined for point lights. The vector # may be normalized, but it needn't. ("mDirection", Vector3D), - + # Up direction of the light source in space. Relative to the # transformation of the node corresponding to the light. # @@ -340,7 +340,7 @@ class Light(Structure): # This member corresponds to the att0 variable in the equation. # Naturally undefined for directional lights. ("mAttenuationConstant", c_float), - + # Linear light attenuation factor. # The intensity of the light source at a given distance 'd' from # the light's position is @@ -352,7 +352,7 @@ class Light(Structure): # This member corresponds to the att1 variable in the equation. # Naturally undefined for directional lights. ("mAttenuationLinear", c_float), - + # Quadratic light attenuation factor. # The intensity of the light source at a given distance 'd' from # the light's position is @@ -364,19 +364,19 @@ class Light(Structure): # This member corresponds to the att2 variable in the equation. # Naturally undefined for directional lights. ("mAttenuationQuadratic", c_float), - + # Diffuse color of the light source # The diffuse light color is multiplied with the diffuse # material color to obtain the final color that contributes # to the diffuse shading term. ("mColorDiffuse", Color3D), - + # Specular color of the light source # The specular light color is multiplied with the specular # material color to obtain the final color that contributes # to the specular shading term. ("mColorSpecular", Color3D), - + # Ambient color of the light source # The ambient light color is multiplied with the ambient # material color to obtain the final color that contributes @@ -384,13 +384,13 @@ class Light(Structure): # this value it, is just a remaining of the fixed-function pipeline # that is still supported by quite many file formats. ("mColorAmbient", Color3D), - + # Inner angle of a spot light's light cone. # The spot light has maximum influence on objects inside this # angle. The angle is given in radians. It is 2PI for point # lights and undefined for directional lights. ("mAngleInnerCone", c_float), - + # Outer angle of a spot light's light cone. # The spot light does not affect objects outside this angle. # The angle is given in radians. It is 2PI for point lights and @@ -408,7 +408,7 @@ class Light(Structure): class Texture(Structure): """ See 'texture.h' for details. - """ + """ _fields_ = [ @@ -417,15 +417,15 @@ class Texture(Structure): # like JPEG. In this case mWidth specifies the size of the # memory area pcData is pointing to, in bytes. ("mWidth", c_uint), - + # Height of the texture, in pixels # If this value is zero, pcData points to an compressed texture # in any format (e.g. JPEG). ("mHeight", c_uint), - + # A hint from the loader to make it easier for applications # to determine the type of embedded textures. - # + # # If mHeight != 0 this member is show how data is packed. Hint will consist of # two parts: channel order and channel bitness (count of the bits for every # color channel). For simple parsing by the viewer it's better to not omit @@ -443,7 +443,7 @@ class Texture(Structure): # E.g. 'dds\\0', 'pcx\\0', 'jpg\\0'. All characters are lower-case. # The fourth character will always be '\\0'. ("achFormatHint", c_char*9), - + # Data of the texture. # Points to an array of mWidth # mHeight aiTexel's. @@ -462,7 +462,7 @@ class Texture(Structure): class Ray(Structure): """ See 'types.h' for details. - """ + """ _fields_ = [ # Position and direction of the ray @@ -472,17 +472,17 @@ class Ray(Structure): class UVTransform(Structure): """ See 'material.h' for details. - """ + """ _fields_ = [ # Translation on the u and v axes. # The default value is (0|0). ("mTranslation", Vector2D), - + # Scaling on the u and v axes. # The default value is (1|1). ("mScaling", Vector2D), - + # Rotation - in counter-clockwise direction. # The rotation angle is specified in radians. The # rotation center is 0.5f|0.5f. The default value @@ -493,34 +493,34 @@ class UVTransform(Structure): class MaterialProperty(Structure): """ See 'material.h' for details. - """ + """ _fields_ = [ # Specifies the name of the property (key) # Keys are generally case insensitive. ("mKey", String), - + # Textures: Specifies their exact usage semantic. # For non-texture properties, this member is always 0 # (or, better-said, #aiTextureType_NONE). ("mSemantic", c_uint), - + # Textures: Specifies the index of the texture. # For non-texture properties, this member is always 0. ("mIndex", c_uint), - + # Size of the buffer mData is pointing to, in bytes. # This value may not be 0. ("mDataLength", c_uint), - + # Type information for the property. # Defines the data layout inside the data buffer. This is used # by the library internally to perform debug checks and to # utilize proper type conversions. # (It's probably a hacky solution, but it works.) ("mType", c_uint), - + # Binary buffer to hold the property's value. # The size of the buffer is always mDataLength. ("mData", POINTER(c_char)), @@ -529,15 +529,15 @@ class MaterialProperty(Structure): class Material(Structure): """ See 'material.h' for details. - """ + """ _fields_ = [ # List of all material properties loaded. ("mProperties", POINTER(POINTER(MaterialProperty))), - + # Number of properties in the data base ("mNumProperties", c_uint), - + # Storage allocated ("mNumAllocated", c_uint), ] @@ -545,20 +545,20 @@ class Material(Structure): class Bone(Structure): """ See 'mesh.h' for details. - """ + """ _fields_ = [ # The name of the bone. ("mName", String), - + # The number of vertices affected by this bone # The maximum value for this member is #AI_MAX_BONE_WEIGHTS. ("mNumWeights", c_uint), - + # The vertices affected by this bone ("mWeights", POINTER(VertexWeight)), - + # Matrix that transforms from mesh space to bone space in bind pose ("mOffsetMatrix", Matrix4x4), ] @@ -567,7 +567,7 @@ class Bone(Structure): class AnimMesh(Structure): """ See 'mesh.h' for details. - """ + """ AI_MAX_NUMBER_OF_TEXTURECOORDS = 0x8 AI_MAX_NUMBER_OF_COLOR_SETS = 0x8 @@ -613,7 +613,7 @@ class AnimMesh(Structure): class Mesh(Structure): """ See 'mesh.h' for details. - """ + """ AI_MAX_FACE_INDICES = 0x7fff AI_MAX_BONE_WEIGHTS = 0x7fffffff @@ -628,24 +628,24 @@ class Mesh(Structure): # The "SortByPrimitiveType"-Step can be used to make sure the # output meshes consist of one primitive type each. ("mPrimitiveTypes", c_uint), - + # The number of vertices in this mesh. # This is also the size of all of the per-vertex data arrays. # The maximum value for this member is #AI_MAX_VERTICES. ("mNumVertices", c_uint), - + # The number of primitives (triangles, polygons, lines) in this mesh. # This is also the size of the mFaces array. # The maximum value for this member is #AI_MAX_FACES. ("mNumFaces", c_uint), - + # Vertex positions. # This array is always present in a mesh. The array is # mNumVertices in size. ("mVertices", POINTER(Vector3D)), - + # Vertex normals. # The array contains normalized vectors, NULL if not present. # The array is mNumVertices in size. Normals are undefined for @@ -666,7 +666,7 @@ class Mesh(Structure): # However, this needn't apply for normals that have been taken # directly from the model file. ("mNormals", POINTER(Vector3D)), - + # Vertex tangents. # The tangent of a vertex points in the direction of the positive # X texture axis. The array contains normalized vectors, NULL if @@ -681,7 +681,7 @@ class Mesh(Structure): # contains bitangents (the bitangent is just the cross product of # tangent and normal vectors). ("mTangents", POINTER(Vector3D)), - + # Vertex bitangents. # The bitangent of a vertex points in the direction of the positive # Y texture axis. The array contains normalized vectors, NULL if not @@ -689,19 +689,19 @@ class Mesh(Structure): # @note If the mesh contains tangents, it automatically also contains # bitangents. ("mBitangents", POINTER(Vector3D)), - + # Vertex color sets. # A mesh may contain 0 to #AI_MAX_NUMBER_OF_COLOR_SETS vertex # colors per vertex. NULL if not present. Each array is # mNumVertices in size if present. ("mColors", POINTER(Color4D)*AI_MAX_NUMBER_OF_COLOR_SETS), - + # Vertex texture coords, also known as UV channels. # A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per # vertex. NULL if not present. The array is mNumVertices in size. ("mTextureCoords", POINTER(Vector3D)*AI_MAX_NUMBER_OF_TEXTURECOORDS), - + # Specifies the number of components for a given UV channel. # Up to three channels are supported (UVW, for accessing volume # or cube maps). If the value is 2 for a given channel n, the @@ -709,7 +709,7 @@ class Mesh(Structure): # If the value is 1 for a given channel, p.y is set to 0.0f, too. # @note 4D coords are not supported ("mNumUVComponents", c_uint*AI_MAX_NUMBER_OF_TEXTURECOORDS), - + # The faces the mesh is constructed from. # Each face refers to a number of vertices by their indices. # This array is always present in a mesh, its size is given @@ -717,22 +717,22 @@ class Mesh(Structure): #AI_SCENE_FLAGS_NON_VERBOSE_FORMAT # is NOT set each face references an unique set of vertices. ("mFaces", POINTER(Face)), - + # The number of bones this mesh contains. # Can be 0, in which case the mBones array is NULL. ("mNumBones", c_uint), - + # The bones of this mesh. # A bone consists of a name by which it can be found in the # frame hierarchy and a set of vertex weights. ("mBones", POINTER(POINTER(Bone))), - + # The material used by this mesh. # A mesh does use only a single material. If an imported model uses # multiple materials, the import splits up the mesh. Use this value # as index into the scene's material list. ("mMaterialIndex", c_uint), - + # Name of the mesh. Meshes can be named, but this is not a # requirement and leaving this field empty is totally fine. # There are mainly three uses for mesh names: @@ -744,10 +744,10 @@ class Mesh(Structure): # partitioning. # - Vertex animations refer to meshes by their names. ("mName", String), - + # The number of attachment meshes. Note! Currently only works with Collada loader. ("mNumAnimMeshes", c_uint), - + # Attachment meshes for this mesh, for vertex-based animation. # Attachment meshes carry replacement data for some of the # mesh'es vertex components (usually positions, normals). @@ -762,7 +762,7 @@ class Mesh(Structure): class Camera(Structure): """ See 'camera.h' for details. - """ + """ _fields_ = [ @@ -771,12 +771,12 @@ class Camera(Structure): # This node specifies the position of the camera in the scene # hierarchy and can be animated. ("mName", String), - + # Position of the camera relative to the coordinate space # defined by the corresponding node. # The default value is 0|0|0. ("mPosition", Vector3D), - + # 'Up' - vector of the camera coordinate system relative to # the coordinate space defined by the corresponding node. # The 'right' vector of the camera coordinate system is @@ -784,25 +784,25 @@ class Camera(Structure): # The default value is 0|1|0. The vector # may be normalized, but it needn't. ("mUp", Vector3D), - + # 'LookAt' - vector of the camera coordinate system relative to # the coordinate space defined by the corresponding node. # This is the viewing direction of the user. # The default value is 0|0|1. The vector # may be normalized, but it needn't. ("mLookAt", Vector3D), - + # Half horizontal field of view angle, in radians. # The field of view angle is the angle between the center # line of the screen and the left or right border. # The default value is 1/4PI. ("mHorizontalFOV", c_float), - + # Distance of the near clipping plane from the camera. # The value may not be 0.f (for arithmetic reasons to prevent # a division through zero). The default value is 0.1f. ("mClipPlaneNear", c_float), - + # Distance of the far clipping plane from the camera. # The far clipping plane must, of course, be further away than the # near clipping plane. The default value is 1000.f. The ratio @@ -810,7 +810,7 @@ class Camera(Structure): # large (between 1000-10000 should be ok) to avoid floating-point # inaccuracies which could lead to z-fighting. ("mClipPlaneFar", c_float), - + # Screen aspect ratio. # This is the ration between the width and the height of the # screen. Typical values are 4/3, 1/2 or 1/1. This value is @@ -822,12 +822,12 @@ class Camera(Structure): class VectorKey(Structure): """ See 'anim.h' for details. - """ + """ _fields_ = [ # The time of this key ("mTime", c_double), - + # The value of this key ("mValue", Vector3D), ] @@ -835,12 +835,12 @@ class VectorKey(Structure): class QuatKey(Structure): """ See 'anim.h' for details. - """ + """ _fields_ = [ # The time of this key ("mTime", c_double), - + # The value of this key ("mValue", Quaternion), ] @@ -848,7 +848,7 @@ class QuatKey(Structure): class MeshMorphKey(Structure): """ See 'anim.h' for details. - """ + """ _fields_ = [ # The time of this key @@ -866,47 +866,47 @@ class MeshMorphKey(Structure): class NodeAnim(Structure): """ See 'anim.h' for details. - """ + """ _fields_ = [ # The name of the node affected by this animation. The node # must exist and it must be unique. ("mNodeName", String), - + # The number of position keys ("mNumPositionKeys", c_uint), - + # The position keys of this animation channel. Positions are # specified as 3D vector. The array is mNumPositionKeys in size. # If there are position keys, there will also be at least one # scaling and one rotation key. ("mPositionKeys", POINTER(VectorKey)), - + # The number of rotation keys ("mNumRotationKeys", c_uint), - + # The rotation keys of this animation channel. Rotations are # given as quaternions, which are 4D vectors. The array is # mNumRotationKeys in size. # If there are rotation keys, there will also be at least one # scaling and one position key. ("mRotationKeys", POINTER(QuatKey)), - + # The number of scaling keys ("mNumScalingKeys", c_uint), - + # The scaling keys of this animation channel. Scalings are # specified as 3D vector. The array is mNumScalingKeys in size. # If there are scaling keys, there will also be at least one # position and one rotation key. ("mScalingKeys", POINTER(VectorKey)), - + # Defines how the animation behaves before the first # key is encountered. # The default value is aiAnimBehaviour_DEFAULT (the original # transformation matrix of the affected node is used). ("mPreState", c_uint), - + # Defines how the animation behaves after the last # key was processed. # The default value is aiAnimBehaviour_DEFAULT (the original @@ -917,7 +917,7 @@ class NodeAnim(Structure): class MeshAnim(Structure): """ See 'anim.h' for details. - """ + """ _fields_ = [ # Name of the mesh to be animated. An empty string is not allowed, @@ -936,7 +936,7 @@ class MeshAnim(Structure): class MeshMorphAnim(Structure): """ See 'anim.h' for details. - """ + """ _fields_ = [ # Name of the mesh to be animated. An empty string is not allowed, @@ -956,32 +956,32 @@ class MeshMorphAnim(Structure): class Animation(Structure): """ See 'anim.h' for details. - """ + """ _fields_ = [ # The name of the animation. If the modeling package this data was # exported from does support only a single animation channel, this # name is usually empty (length is zero). ("mName", String), - + # Duration of the animation in ticks. ("mDuration", c_double), - + # Ticks per second. 0 if not specified in the imported file ("mTicksPerSecond", c_double), - + # The number of bone animation channels. Each channel affects # a single node. ("mNumChannels", c_uint), - + # The node animation channels. Each channel affects a single node. # The array is mNumChannels in size. ("mChannels", POINTER(POINTER(NodeAnim))), - + # The number of mesh animation channels. Each channel affects # a single mesh and defines vertex-based animation. ("mNumMeshChannels", c_uint), - + # The mesh animation channels. Each channel affects a single mesh. # The array is mNumMeshChannels in size. ("mMeshChannels", POINTER(POINTER(MeshAnim))), @@ -991,7 +991,7 @@ class Animation(Structure): ("mNumMorphMeshChannels", c_uint), # The morph mesh animation channels. Each channel affects a single mesh. - # The array is mNumMorphMeshChannels in size. + # The array is mNumMorphMeshChannels in size. ("mMorphMeshChannels", POINTER(POINTER(MeshMorphAnim))), ] @@ -1032,7 +1032,7 @@ ExportDataBlob._fields_ = [ class Scene(Structure): """ See 'aiScene.h' for details. - """ + """ AI_SCENE_FLAGS_INCOMPLETE = 0x1 AI_SCENE_FLAGS_VALIDATED = 0x2 @@ -1047,64 +1047,64 @@ class Scene(Structure): # want to reject all scenes with the AI_SCENE_FLAGS_INCOMPLETE # bit set. ("mFlags", c_uint), - + # The root node of the hierarchy. # There will always be at least the root node if the import # was successful (and no special flags have been set). # Presence of further nodes depends on the format and content # of the imported file. ("mRootNode", POINTER(Node)), - + # The number of meshes in the scene. ("mNumMeshes", c_uint), - + # The array of meshes. # Use the indices given in the aiNode structure to access # this array. The array is mNumMeshes in size. If the # AI_SCENE_FLAGS_INCOMPLETE flag is not set there will always # be at least ONE material. ("mMeshes", POINTER(POINTER(Mesh))), - + # The number of materials in the scene. ("mNumMaterials", c_uint), - + # The array of materials. # Use the index given in each aiMesh structure to access this # array. The array is mNumMaterials in size. If the # AI_SCENE_FLAGS_INCOMPLETE flag is not set there will always # be at least ONE material. ("mMaterials", POINTER(POINTER(Material))), - + # The number of animations in the scene. ("mNumAnimations", c_uint), - + # The array of animations. # All animations imported from the given file are listed here. # The array is mNumAnimations in size. ("mAnimations", POINTER(POINTER(Animation))), - + # The number of textures embedded into the file ("mNumTextures", c_uint), - + # The array of embedded textures. # Not many file formats embed their textures into the file. # An example is Quake's MDL format (which is also used by # some GameStudio versions) ("mTextures", POINTER(POINTER(Texture))), - + # The number of light sources in the scene. Light sources # are fully optional, in most cases this attribute will be 0 ("mNumLights", c_uint), - + # The array of light sources. # All light sources imported from the given file are # listed here. The array is mNumLights in size. ("mLights", POINTER(POINTER(Light))), - + # The number of cameras in the scene. Cameras # are fully optional, in most cases this attribute will be 0 ("mNumCameras", c_uint), - + # The array of cameras. # All cameras imported from the given file are listed here. # The array is mNumCameras in size. The first camera in the From d24adbd32c2b9aeb1a40c1bb99c19e9a8e84ad71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Sat, 7 Dec 2019 22:09:03 +0100 Subject: [PATCH 04/36] aiString.length is really of type "ai_uint32" which corresponds to "c_uint32" and not to "c_size_t" (which is different on 64bit systems and 32bit systems!) Closes: https://github.com/assimp/assimp/issues/2788 --- port/PyAssimp/pyassimp/structs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/PyAssimp/pyassimp/structs.py b/port/PyAssimp/pyassimp/structs.py index b93aae68c..b506bc036 100644 --- a/port/PyAssimp/pyassimp/structs.py +++ b/port/PyAssimp/pyassimp/structs.py @@ -76,7 +76,7 @@ class String(Structure): # Binary length of the string excluding the terminal 0. This is NOT the # logical length of strings containing UTF-8 multibyte sequences! It's # the number of bytes from the beginning of the string to its end. - ("length", c_size_t), + ("length", c_uint32), # String buffer. Size limit is MAXLEN ("data", c_char*MAXLEN), From 630f013a5e70834f6e02cf1489b633c8b2325734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Sat, 7 Dec 2019 22:10:26 +0100 Subject: [PATCH 05/36] fix field name "Mesh.mAnimMesh" -> "Mesh.mAnimMeshes" to match the name in mesh.h: aiMesh.mAnimMeshes --- port/PyAssimp/pyassimp/structs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/PyAssimp/pyassimp/structs.py b/port/PyAssimp/pyassimp/structs.py index b506bc036..0ac498a44 100644 --- a/port/PyAssimp/pyassimp/structs.py +++ b/port/PyAssimp/pyassimp/structs.py @@ -752,7 +752,7 @@ class Mesh(Structure): # Attachment meshes carry replacement data for some of the # mesh'es vertex components (usually positions, normals). # Note! Currently only works with Collada loader. - ("mAnimMesh", POINTER(POINTER(AnimMesh))), + ("mAnimMeshes", POINTER(POINTER(AnimMesh))), # Method of morphing when animeshes are specified. ("mMethod", c_uint), From 660f3571d8cc0dfb4ea573723938c16ca2c5506f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Mon, 9 Dec 2019 17:23:52 +0100 Subject: [PATCH 06/36] added AnimMesh.mName member Closes: https://github.com/assimp/assimp/issues/2822 --- port/PyAssimp/pyassimp/structs.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/port/PyAssimp/pyassimp/structs.py b/port/PyAssimp/pyassimp/structs.py index 0ac498a44..809afae54 100644 --- a/port/PyAssimp/pyassimp/structs.py +++ b/port/PyAssimp/pyassimp/structs.py @@ -573,6 +573,9 @@ class AnimMesh(Structure): AI_MAX_NUMBER_OF_COLOR_SETS = 0x8 _fields_ = [ + # Anim Mesh name + ("mName", String), + # Replacement for aiMesh::mVertices. If this array is non-NULL, # it *must* contain mNumVertices entries. The corresponding # array in the host mesh must be non-NULL as well - animation From 17aabc34ccd9958f2d67505cd8b290087d978036 Mon Sep 17 00:00:00 2001 From: bzt Date: Tue, 10 Dec 2019 06:30:41 +0100 Subject: [PATCH 07/36] MSVC workarounds --- code/M3D/M3DExporter.cpp | 14 +-- code/M3D/M3DImporter.cpp | 20 +-- code/M3D/m3d.h | 261 ++++++++++++++++++++------------------- 3 files changed, 149 insertions(+), 146 deletions(-) diff --git a/code/M3D/M3DExporter.cpp b/code/M3D/M3DExporter.cpp index 0e84f6d4f..faf5a9585 100644 --- a/code/M3D/M3DExporter.cpp +++ b/code/M3D/M3DExporter.cpp @@ -134,7 +134,7 @@ void addProp(m3dm_t *m, uint8_t type, uint32_t value) { // ------------------------------------------------------------------------------------------------ // add a material to the output M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) { - unsigned int mi = -1U; + unsigned int mi = M3D_NOTDEFINED; aiColor4D c; aiString name; ai_real f; @@ -150,7 +150,7 @@ M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) { break; } // if not found, add the material to the output - if (mi == -1U) { + if (mi == M3D_NOTDEFINED) { unsigned int k; mi = m3d->nummaterial++; m3d->material = (m3dm_t *)M3D_REALLOC(m3d->material, m3d->nummaterial * sizeof(m3dm_t)); @@ -219,13 +219,13 @@ M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) { name.data[j] = 0; // do we have this texture saved already? fn = _m3d_safestr((char *)&name.data, 0); - for (j = 0, i = -1U; j < m3d->numtexture; j++) + for (j = 0, i = M3D_NOTDEFINED; j < m3d->numtexture; j++) if (!strcmp(fn, m3d->texture[j].name)) { i = j; free(fn); break; } - if (i == -1U) { + if (i == M3D_NOTDEFINED) { i = m3d->numtexture++; m3d->texture = (m3dtx_t *)M3D_REALLOC( m3d->texture, @@ -335,7 +335,7 @@ void M3DExporter::NodeWalk(const M3DWrapper &m3d, const aiNode *pNode, aiMatrix4 for (unsigned int i = 0; i < pNode->mNumMeshes; i++) { const aiMesh *mesh = mScene->mMeshes[pNode->mMeshes[i]]; - unsigned int mi = (M3D_INDEX)-1U; + unsigned int mi = M3D_NOTDEFINED; if (mScene->mMaterials) { // get the material for this mesh mi = addMaterial(m3d, mScene->mMaterials[mesh->mMaterialIndex]); @@ -358,7 +358,7 @@ void M3DExporter::NodeWalk(const M3DWrapper &m3d, const aiNode *pNode, aiMatrix4 /* set all index to -1 by default */ m3d->face[n].vertex[0] = m3d->face[n].vertex[1] = m3d->face[n].vertex[2] = m3d->face[n].normal[0] = m3d->face[n].normal[1] = m3d->face[n].normal[2] = - m3d->face[n].texcoord[0] = m3d->face[n].texcoord[1] = m3d->face[n].texcoord[2] = -1U; + m3d->face[n].texcoord[0] = m3d->face[n].texcoord[1] = m3d->face[n].texcoord[2] = M3D_UNDEF; m3d->face[n].materialid = mi; for (unsigned int k = 0; k < face->mNumIndices; k++) { // get the vertex's index @@ -374,7 +374,7 @@ void M3DExporter::NodeWalk(const M3DWrapper &m3d, const aiNode *pNode, aiMatrix4 vertex.z = v.z; vertex.w = 1.0; vertex.color = 0; - vertex.skinid = -1U; + vertex.skinid = M3D_UNDEF; // add color if defined if (mesh->HasVertexColors(0)) vertex.color = mkColor(&mesh->mColors[0][l]); diff --git a/code/M3D/M3DImporter.cpp b/code/M3D/M3DImporter.cpp index 5218dd9ed..6627d1f74 100644 --- a/code/M3D/M3DImporter.cpp +++ b/code/M3D/M3DImporter.cpp @@ -176,7 +176,7 @@ void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSys // let the C SDK do the hard work for us M3DWrapper m3d(pIOHandler, buffer); - + if (!m3d) { throw DeadlyImportError("Unable to parse " + file + " as M3D."); } @@ -193,7 +193,7 @@ void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSys // now we just have to fill up the Assimp structures in pScene importMaterials(m3d); importTextures(m3d); - importBones(m3d, -1U, pScene->mRootNode); + importBones(m3d, M3D_NOTDEFINED, pScene->mRootNode); importMeshes(m3d); importAnimations(m3d); @@ -343,7 +343,7 @@ void M3DImporter::importTextures(const M3DWrapper &m3d) { // individually. In assimp there're per mesh vertex and UV lists, and they must be // indexed simultaneously. void M3DImporter::importMeshes(const M3DWrapper &m3d) { - unsigned int i, j, k, l, numpoly = 3, lastMat = -2U; + unsigned int i, j, k, l, numpoly = 3, lastMat = M3D_INDEXMAX; std::vector *meshes = new std::vector(); std::vector *faces = nullptr; std::vector *vertices = nullptr; @@ -398,20 +398,20 @@ void M3DImporter::importMeshes(const M3DWrapper &m3d) { vertices->push_back(pos); colors->push_back(mkColor(m3d->vertex[l].color)); // add a bone to temporary vector - if (m3d->vertex[l].skinid != -1U && m3d->vertex[l].skinid != -2U && m3d->skin && m3d->bone) { + if (m3d->vertex[l].skinid != M3D_UNDEF && m3d->vertex[l].skinid != M3D_INDEXMAX && m3d->skin && m3d->bone) { // this is complicated, because M3D stores a list of bone id / weight pairs per // vertex but assimp uses lists of local vertex id/weight pairs per local bone list vertexids->push_back(l); } l = m3d->face[i].texcoord[j]; - if (l != -1U) { + if (l != M3D_UNDEF) { uv.x = m3d->tmap[l].u; uv.y = m3d->tmap[l].v; uv.z = 0.0; texcoords->push_back(uv); } l = m3d->face[i].normal[j]; - if (l != -1U) { + if (l != M3D_UNDEF) { norm.x = m3d->vertex[l].x; norm.y = m3d->vertex[l].y; norm.z = m3d->vertex[l].z; @@ -557,8 +557,8 @@ aiColor4D M3DImporter::mkColor(uint32_t c) { void M3DImporter::convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid) { ai_assert(m != nullptr); ai_assert(m3d); - ai_assert(posid != -1U && posid < m3d->numvertex); - ai_assert(orientid != -1U && orientid < m3d->numvertex); + ai_assert(posid != M3D_UNDEF && posid < m3d->numvertex); + ai_assert(orientid != M3D_UNDEF && orientid < m3d->numvertex); m3dv_t *p = &m3d->vertex[posid]; m3dv_t *q = &m3d->vertex[orientid]; @@ -692,7 +692,7 @@ void M3DImporter::populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector // first count how many vertices we have per bone for (i = 0; i < vertexids->size(); i++) { unsigned int s = m3d->vertex[vertexids->at(i)].skinid; - if (s != -1U && s != -2U) { + if (s != M3D_UNDEF && s != M3D_INDEXMAX) { for (unsigned int k = 0; k < M3D_NUMBONE && m3d->skin[s].weight[k] > 0.0; k++) { aiString name = aiString(std::string(m3d->bone[m3d->skin[s].boneid[k]].name)); for (j = 0; j < pMesh->mNumBones; j++) { @@ -715,7 +715,7 @@ void M3DImporter::populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector // fill up with data for (i = 0; i < vertexids->size(); i++) { unsigned int s = m3d->vertex[vertexids->at(i)].skinid; - if (s != -1U && s != -2U) { + if (s != M3D_UNDEF && s != M3D_INDEXMAX) { for (unsigned int k = 0; k < M3D_NUMBONE && m3d->skin[s].weight[k] > 0.0; k++) { aiString name = aiString(std::string(m3d->bone[m3d->skin[s].boneid[k]].name)); for (j = 0; j < pMesh->mNumBones; j++) { diff --git a/code/M3D/m3d.h b/code/M3D/m3d.h index c1d690bcc..d1fedc5b5 100644 --- a/code/M3D/m3d.h +++ b/code/M3D/m3d.h @@ -70,11 +70,14 @@ typedef double M3D_FLOAT; #endif #if !defined(M3D_SMALLINDEX) typedef uint32_t M3D_INDEX; +#define M3D_UNDEF 0xffffffff #define M3D_INDEXMAX 0xfffffffe #else typedef uint16_t M3D_INDEX; +#define M3D_UNDEF 0xffff #define M3D_INDEXMAX 0xfffe #endif +#define M3D_NOTDEFINED 0xffffffff #ifndef M3D_NUMBONE #define M3D_NUMBONE 4 #endif @@ -2123,10 +2126,10 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char } /* try to load from external source */ if(!buff && readfilecb) { - i = strlen(fn); + i = (unsigned int)strlen(fn); if(i < 5 || fn[i - 4] != '.') { fn2 = (char*)M3D_MALLOC(i + 5); - if(!fn2) { model->errcode = M3D_ERR_ALLOC; return (M3D_INDEX)-1U; } + if(!fn2) { model->errcode = M3D_ERR_ALLOC; return M3D_UNDEF; } memcpy(fn2, fn, i); memcpy(fn2+i, ".png", 5); buff = (*readfilecb)(fn2, &len); @@ -2135,14 +2138,14 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char if(!buff) buff = (*readfilecb)(fn, &len); } - if(!buff) return (M3D_INDEX)-1U; + if(!buff) return M3D_UNDEF; /* add to textures array */ i = model->numtexture++; model->texture = (m3dtx_t*)M3D_REALLOC(model->texture, model->numtexture * sizeof(m3dtx_t)); if(!model->texture) { if(freecb) (*freecb)(buff); model->errcode = M3D_ERR_ALLOC; - return (M3D_INDEX)-1U; + return M3D_UNDEF; } model->texture[i].name = fn; model->texture[i].w = model->texture[i].h = 0; model->texture[i].d = NULL; @@ -2434,7 +2437,7 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t)); if(!model->vertex) goto memerr; memset(&model->vertex[i], 0, sizeof(m3dv_t)); - model->vertex[i].skinid = (M3D_INDEX)-1U; + model->vertex[i].skinid = M3D_UNDEF; model->vertex[i].color = 0; model->vertex[i].w = (M3D_FLOAT)1.0; ptr = _m3d_getfloat(ptr, &model->vertex[i].x); @@ -2464,16 +2467,16 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d s.weight[j] = (M3D_FLOAT)1.0; if(!*ptr) goto asciiend; } - if(s.boneid[0] != (M3D_INDEX)-1U && s.weight[0] > (M3D_FLOAT)0.0) { + if(s.boneid[0] != M3D_UNDEF && s.weight[0] > (M3D_FLOAT)0.0) { if(w != (M3D_FLOAT)1.0 && w != (M3D_FLOAT)0.0) for(j = 0; j < M3D_NUMBONE && s.weight[j] > (M3D_FLOAT)0.0; j++) s.weight[j] /= w; - k = -1U; + k = M3D_NOTDEFINED; if(model->skin) { for(j = 0; j < model->numskin; j++) if(!memcmp(&model->skin[j], &s, sizeof(m3ds_t))) { k = j; break; } } - if(k == -1U) { + if(k == M3D_NOTDEFINED) { k = model->numskin++; model->skin = (m3ds_t*)M3D_REALLOC(model->skin, model->numskin * sizeof(m3ds_t)); memcpy(&model->skin[k], &s, sizeof(m3ds_t)); @@ -2486,7 +2489,7 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d /* Skeleton, bone hierarchy */ if(!memcmp(pe, "Bones", 5)) { if(model->bone) { M3D_LOG("More bones chunks, should be unique"); goto asciiend; } - bi[0] = (M3D_INDEX)-1U; + bi[0] = M3D_UNDEF; while(*ptr && *ptr != '\r' && *ptr != '\n') { i = model->numbone++; model->bone = (m3db_t*)M3D_REALLOC(model->bone, model->numbone * sizeof(m3db_t)); @@ -2505,7 +2508,7 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d ptr = _m3d_findarg(ptr); if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; model->bone[i].ori = (M3D_INDEX)k; - model->vertex[k].skinid = (M3D_INDEX)-2U; + model->vertex[k].skinid = M3D_INDEXMAX; pe = _m3d_safestr(ptr, 0); if(!pe || !*pe) goto asciiend; model->bone[i].name = pe; @@ -2579,7 +2582,7 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d if(!pe || !*pe) goto asciiend; m->prop[j].value.textureid = _m3d_gettx(model, readfilecb, freecb, pe); if(model->errcode == M3D_ERR_ALLOC) { M3D_FREE(pe); goto memerr; } - if(m->prop[j].value.textureid == (M3D_INDEX)-1U) { + if(m->prop[j].value.textureid == M3D_UNDEF) { M3D_LOG("Texture not found"); M3D_LOG(pe); m->numprop--; @@ -2605,18 +2608,18 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d } else /* mesh */ if(!memcmp(pe, "Mesh", 4)) { - mi = (M3D_INDEX)-1U; + mi = M3D_UNDEF; while(*ptr && *ptr != '\r' && *ptr != '\n') { if(*ptr == 'u') { ptr = _m3d_findarg(ptr); if(!*ptr) goto asciiend; - mi = (M3D_INDEX)-1U; + mi = M3D_UNDEF; if(*ptr != '\r' && *ptr != '\n') { pe = _m3d_safestr(ptr, 0); if(!pe || !*pe) goto asciiend; for(j = 0; j < model->nummaterial; j++) if(!strcmp(pe, model->material[j].name)) { mi = (M3D_INDEX)j; break; } - if(mi == (M3D_INDEX)-1U && !(model->flags & M3D_FLG_MTLLIB)) { + if(mi == M3D_UNDEF && !(model->flags & M3D_FLG_MTLLIB)) { mi = model->nummaterial++; model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t)); if(!model->material) goto memerr; @@ -2655,7 +2658,7 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d } } #ifndef M3D_NONORMALS - if(model->face[i].normal[j] == (M3D_INDEX)-1U) neednorm = 1; + if(model->face[i].normal[j] == M3D_UNDEF) neednorm = 1; #endif ptr = _m3d_findarg(ptr); } @@ -2674,7 +2677,7 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d if(!model->shape) goto memerr; h = &model->shape[i]; h->name = pe; - h->group = (M3D_INDEX)-1U; + h->group = M3D_UNDEF; h->numcmd = 0; h->cmd = NULL; while(*ptr && *ptr != '\r' && *ptr != '\n') { @@ -2682,16 +2685,16 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d ptr = _m3d_findarg(ptr); ptr = _m3d_getint(ptr, &h->group); ptr = _m3d_findnl(ptr); - if(h->group != (M3D_INDEX)-1U && h->group >= model->numbone) { + if(h->group != M3D_UNDEF && h->group >= model->numbone) { M3D_LOG("Unknown bone id as shape group in shape"); M3D_LOG(pe); - h->group = (M3D_INDEX)-1U; + h->group = M3D_UNDEF; model->errcode = M3D_ERR_SHPE; } continue; } for(cd = NULL, k = 0; k < (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])); k++) { - j = strlen(m3d_commandtypes[k].key); + j = (unsigned int)strlen(m3d_commandtypes[k].key); if(!memcmp(ptr, m3d_commandtypes[k].key, j) && (ptr[j] == ' ' || ptr[j] == '\r' || ptr[j] == '\n')) { cd = &m3d_commandtypes[k]; break; } } @@ -2713,13 +2716,13 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d if(*ptr == ']' || *ptr == '\r' || *ptr == '\n') break; switch(cd->a[((k - n) % (cd->p - n)) + n]) { case m3dcp_mi_t: - mi = (M3D_INDEX)-1U; + mi = M3D_UNDEF; if(*ptr != '\r' && *ptr != '\n') { pe = _m3d_safestr(ptr, 0); if(!pe || !*pe) goto asciiend; for(n = 0; n < model->nummaterial; n++) if(!strcmp(pe, model->material[n].name)) { mi = (M3D_INDEX)n; break; } - if(mi == (M3D_INDEX)-1U && !(model->flags & M3D_FLG_MTLLIB)) { + if(mi == M3D_UNDEF && !(model->flags & M3D_FLG_MTLLIB)) { mi = model->nummaterial++; model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t)); @@ -2745,7 +2748,7 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d break; case m3dcp_qi_t: ptr = _m3d_getint(ptr, &h->cmd[j].arg[k]); - model->vertex[h->cmd[i].arg[k]].skinid = (M3D_INDEX)-2U; + model->vertex[h->cmd[i].arg[k]].skinid = M3D_INDEXMAX; break; default: ptr = _m3d_getint(ptr, &h->cmd[j].arg[k]); @@ -2844,7 +2847,7 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d ptr = _m3d_getint(ptr, &k); if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; a->frame[i].transform[j].ori = (M3D_INDEX)k; - model->vertex[k].skinid = (M3D_INDEX)-2U; + model->vertex[k].skinid = M3D_INDEXMAX; } ptr = _m3d_findnl(ptr); } @@ -3116,7 +3119,7 @@ memerr: M3D_LOG("Out of memory"); case 4: model->vertex[i].color = *((uint32_t*)data); data += 4; break; /* case 8: break; */ } - model->vertex[i].skinid = (M3D_INDEX)-1U; + model->vertex[i].skinid = M3D_UNDEF; data = _m3d_getidx(data, model->sk_s, &model->vertex[i].skinid); } } else @@ -3150,7 +3153,7 @@ memerr: M3D_LOG("Out of memory"); if(!model->skin) goto memerr; for(i = 0; data < chunk && i < model->numskin; i++) { for(j = 0; j < M3D_NUMBONE; j++) { - model->skin[i].boneid[j] = (M3D_INDEX)-1U; + model->skin[i].boneid[j] = M3D_UNDEF; model->skin[i].weight[j] = (M3D_FLOAT)0.0; } memset(&weights, 0, sizeof(weights)); @@ -3244,7 +3247,7 @@ memerr: M3D_LOG("Out of memory"); M3D_GETSTR(name); m->prop[i].value.textureid = _m3d_gettx(model, readfilecb, freecb, name); if(model->errcode == M3D_ERR_ALLOC) goto memerr; - if(m->prop[i].value.textureid == (M3D_INDEX)-1U) { + if(m->prop[i].value.textureid == M3D_UNDEF) { M3D_LOG("Texture not found"); M3D_LOG(m->name); m->numprop--; @@ -3275,7 +3278,7 @@ memerr: M3D_LOG("Out of memory"); M3D_LOG("Mesh data"); /* mesh */ data += sizeof(m3dchunk_t); - mi = (M3D_INDEX)-1U; + mi = M3D_UNDEF; am = model->numface; while(data < chunk) { k = *data++; @@ -3283,7 +3286,7 @@ memerr: M3D_LOG("Out of memory"); k &= 15; if(!n) { /* use material */ - mi = (M3D_INDEX)-1U; + mi = M3D_UNDEF; M3D_GETSTR(name); if(name) { for(j = 0; j < model->nummaterial; j++) @@ -3291,7 +3294,7 @@ memerr: M3D_LOG("Out of memory"); mi = (M3D_INDEX)j; break; } - if(mi == (M3D_INDEX)-1U) model->errcode = M3D_ERR_MTRL; + if(mi == M3D_UNDEF) model->errcode = M3D_ERR_MTRL; } continue; } @@ -3314,7 +3317,7 @@ memerr: M3D_LOG("Out of memory"); if(k & 2) data = _m3d_getidx(data, model->vi_s, &model->face[i].normal[j]); #ifndef M3D_NONORMALS - if(model->face[i].normal[j] == (M3D_INDEX)-1U) neednorm = 1; + if(model->face[i].normal[j] == M3D_UNDEF) neednorm = 1; #endif } } @@ -3333,12 +3336,12 @@ memerr: M3D_LOG("Out of memory"); h->numcmd = 0; h->cmd = NULL; h->name = name; - h->group = (M3D_INDEX)-1U; + h->group = M3D_UNDEF; data = _m3d_getidx(data, model->bi_s, &h->group); - if(h->group != (M3D_INDEX)-1U && h->group >= model->numbone) { + if(h->group != M3D_UNDEF && h->group >= model->numbone) { M3D_LOG("Unknown bone id as shape group in shape"); M3D_LOG(name); - h->group = (M3D_INDEX)-1U; + h->group = M3D_UNDEF; model->errcode = M3D_ERR_SHPE; } while(data < chunk) { @@ -3363,7 +3366,7 @@ memerr: M3D_LOG("Out of memory"); for(k = n = 0, l = cd->p; k < l; k++) switch(cd->a[((k - n) % (cd->p - n)) + n]) { case m3dcp_mi_t: - h->cmd[i].arg[k] = -1U; + h->cmd[i].arg[k] = M3D_NOTDEFINED; M3D_GETSTR(name); if(name) { for(n = 0; n < model->nummaterial; n++) @@ -3371,7 +3374,7 @@ memerr: M3D_LOG("Out of memory"); h->cmd[i].arg[k] = n; break; } - if(h->cmd[i].arg[k] == -1U) model->errcode = M3D_ERR_MTRL; + if(h->cmd[i].arg[k] == M3D_NOTDEFINED) model->errcode = M3D_ERR_MTRL; } break; case m3dcp_vc_t: @@ -3488,7 +3491,7 @@ postprocess: norm = (m3dv_t*)M3D_MALLOC(model->numface * sizeof(m3dv_t)); if(!norm) goto memerr; for(i = 0, n = model->numvertex; i < model->numface; i++) - if(model->face[i].normal[0] == -1U) { + if(model->face[i].normal[0] == M3D_UNDEF) { v0 = &model->vertex[model->face[i].vertex[0]]; v1 = &model->vertex[model->face[i].vertex[1]]; v2 = &model->vertex[model->face[i].vertex[2]]; @@ -3522,7 +3525,7 @@ postprocess: for(i = 0, v0 = &model->vertex[n]; i < n; i++, v0++) { w = _m3d_rsq((v0->x * v0->x) + (v0->y * v0->y) + (v0->z * v0->z)); v0->x *= w; v0->y *= w; v0->z *= w; - v0->skinid = -1U; + v0->skinid = M3D_UNDEF; } M3D_FREE(norm); } @@ -3534,9 +3537,9 @@ postprocess: if(model->vertex[i].skinid < model->numskin) { sk = &model->skin[model->vertex[i].skinid]; w = (M3D_FLOAT)0.0; - for(j = 0; j < M3D_NUMBONE && sk->boneid[j] != (M3D_INDEX)-1U && sk->weight[j] > (M3D_FLOAT)0.0; j++) + for(j = 0; j < M3D_NUMBONE && sk->boneid[j] != M3D_UNDEF && sk->weight[j] > (M3D_FLOAT)0.0; j++) w += sk->weight[j]; - for(j = 0; j < M3D_NUMBONE && sk->boneid[j] != (M3D_INDEX)-1U && sk->weight[j] > (M3D_FLOAT)0.0; j++) { + for(j = 0; j < M3D_NUMBONE && sk->boneid[j] != M3D_UNDEF && sk->weight[j] > (M3D_FLOAT)0.0; j++) { sk->weight[j] /= w; b = &model->bone[sk->boneid[j]]; k = b->numweight++; @@ -3552,7 +3555,7 @@ postprocess: M3D_LOG("Calculating bone transformation matrices"); for(i = 0; i < model->numbone; i++) { b = &model->bone[i]; - if(model->bone[i].parent == (M3D_INDEX)-1U) { + if(model->bone[i].parent == M3D_UNDEF) { _m3d_mat((M3D_FLOAT*)&b->mat4, &model->vertex[b->pos], &model->vertex[b->ori]); } else { _m3d_mat((M3D_FLOAT*)&r, &model->vertex[b->pos], &model->vertex[b->ori]); @@ -3583,7 +3586,7 @@ m3dtr_t *m3d_frame(m3d_t *model, M3D_INDEX actionid, M3D_INDEX frameid, m3dtr_t M3D_INDEX s = frameid; m3dfr_t *fr; - if(!model || !model->numbone || !model->bone || (actionid != (M3D_INDEX)-1U && (!model->action || + if(!model || !model->numbone || !model->bone || (actionid != M3D_UNDEF && (!model->action || actionid >= model->numaction || frameid >= model->action[actionid].numframe))) { model->errcode = M3D_ERR_UNKFRAME; return skeleton; @@ -3597,7 +3600,7 @@ m3dtr_t *m3d_frame(m3d_t *model, M3D_INDEX actionid, M3D_INDEX frameid, m3dtr_t } goto gen; } - if(actionid == (M3D_INDEX)-1U || !frameid) { + if(actionid == M3D_UNDEF || !frameid) { gen: s = 0; for(i = 0; i < model->numbone; i++) { skeleton[i].boneid = i; @@ -3721,7 +3724,7 @@ m3db_t *m3d_pose(m3d_t *model, M3D_INDEX actionid, uint32_t msec) } } for(i = 0; i < model->numbone; i++) { - if(ret[i].parent == (M3D_INDEX)-1U) { + if(ret[i].parent == M3D_UNDEF) { _m3d_mat((M3D_FLOAT*)&ret[i].mat4, &model->vertex[ret[i].pos], &model->vertex[ret[i].ori]); } else { _m3d_mat((M3D_FLOAT*)&r, &model->vertex[ret[i].pos], &model->vertex[ret[i].ori]); @@ -3904,7 +3907,7 @@ m3dhdr_t *_m3d_addhdr(m3dhdr_t *h, m3dstr_t *s) { int i; char *safe = _m3d_safestr(s->str, 0); - i = strlen(safe); + i = (int)strlen(safe); h = (m3dhdr_t*)M3D_REALLOC(h, h->length + i+1); if(!h) { M3D_FREE(safe); return NULL; } memcpy((uint8_t*)h + h->length, safe, i+1); @@ -4033,16 +4036,16 @@ static void _m3d_round(int quality, m3dv_t *src, m3dv_t *dst) /* round according to quality */ switch(quality) { case M3D_EXP_INT8: - t = src->x * 127 + (src->x >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->x = (M3D_FLOAT)t / 127; - t = src->y * 127 + (src->y >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->y = (M3D_FLOAT)t / 127; - t = src->z * 127 + (src->z >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->z = (M3D_FLOAT)t / 127; - t = src->w * 127 + (src->w >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->w = (M3D_FLOAT)t / 127; + t = src->x * 127 + (src->x >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->x = (M3D_FLOAT)t / (M3D_FLOAT)127.0; + t = src->y * 127 + (src->y >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->y = (M3D_FLOAT)t / (M3D_FLOAT)127.0; + t = src->z * 127 + (src->z >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->z = (M3D_FLOAT)t / (M3D_FLOAT)127.0; + t = src->w * 127 + (src->w >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->w = (M3D_FLOAT)t / (M3D_FLOAT)127.0; break; case M3D_EXP_INT16: - t = src->x * 32767 + (src->x >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->x = (M3D_FLOAT)t / 32767; - t = src->y * 32767 + (src->y >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->y = (M3D_FLOAT)t / 32767; - t = src->z * 32767 + (src->z >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->z = (M3D_FLOAT)t / 32767; - t = src->w * 32767 + (src->w >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->w = (M3D_FLOAT)t / 32767; + t = src->x * 32767 + (src->x >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->x = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; + t = src->y * 32767 + (src->y >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->y = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; + t = src->z * 32767 + (src->z >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->z = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; + t = src->w * 32767 + (src->w >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->w = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; break; } if(dst->x == (M3D_FLOAT)-0.0) dst->x = (M3D_FLOAT)0.0; @@ -4160,7 +4163,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size } face[i].opacity = opa[model->face[i].materialid * 2 + 1]; } else - face[i].data.materialid = (M3D_INDEX)-1U; + face[i].data.materialid = M3D_UNDEF; } for(j = 0; j < 3; j++) { k = model->face[i].vertex[j]; @@ -4311,7 +4314,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size if(!(flags & M3D_EXP_NOMATERIAL)) { M3D_LOG("Processing materials"); for(i = k = 0; i < model->nummaterial; i++) { - if(mtrlidx[i] == (M3D_INDEX)-1U || !model->material[i].numprop) continue; + if(mtrlidx[i] == M3D_UNDEF || !model->material[i].numprop) continue; mtrlidx[i] = k++; m = &model->material[i]; str = _m3d_addstr(str, &numstr, m->name); @@ -4345,15 +4348,15 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size tmap = (m3dtisave_t*)M3D_MALLOC(model->numtmap * sizeof(m3dtisave_t)); if(!tmap) goto memerr; for(i = 0; i < model->numtmap; i++) { - if(tmapidx[i] == (M3D_INDEX)-1U) continue; + if(tmapidx[i] == M3D_UNDEF) continue; switch(quality) { case M3D_EXP_INT8: - l = model->tmap[i].u * 255; tcoord.data.u = (M3D_FLOAT)l / 255; - l = model->tmap[i].v * 255; tcoord.data.v = (M3D_FLOAT)l / 255; + l = model->tmap[i].u * 255; tcoord.data.u = (M3D_FLOAT)l / (M3D_FLOAT)255.0; + l = model->tmap[i].v * 255; tcoord.data.v = (M3D_FLOAT)l / (M3D_FLOAT)255.0; break; case M3D_EXP_INT16: - l = model->tmap[i].u * 65535; tcoord.data.u = (M3D_FLOAT)l / 65535; - l = model->tmap[i].v * 65535; tcoord.data.v = (M3D_FLOAT)l / 65535; + l = model->tmap[i].u * 65535; tcoord.data.u = (M3D_FLOAT)l / (M3D_FLOAT)65535.0; + l = model->tmap[i].v * 65535; tcoord.data.v = (M3D_FLOAT)l / (M3D_FLOAT)65535.0; break; default: tcoord.data.u = model->tmap[i].u; @@ -4387,13 +4390,13 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size if(!skin) goto memerr; memset(skinidx, 255, model->numskin * sizeof(M3D_INDEX)); for(i = 0; i < model->numvertex; i++) { - if(vrtxidx[i] != (M3D_INDEX)-1U && model->vertex[i].skinid < model->numskin) + if(vrtxidx[i] != M3D_UNDEF && model->vertex[i].skinid < model->numskin) skinidx[model->vertex[i].skinid] = 0; } for(i = 0; i < model->numskin; i++) { - if(skinidx[i] == (M3D_INDEX)-1U) continue; + if(skinidx[i] == M3D_UNDEF) continue; memset(&sk, 0, sizeof(m3dssave_t)); - for(j = 0, min_x = (M3D_FLOAT)0.0; j < M3D_NUMBONE && model->skin[i].boneid[j] != (M3D_INDEX)-1U && + for(j = 0, min_x = (M3D_FLOAT)0.0; j < M3D_NUMBONE && model->skin[i].boneid[j] != M3D_UNDEF && model->skin[i].weight[j] > (M3D_FLOAT)0.0; j++) { sk.data.boneid[j] = model->skin[i].boneid[j]; sk.data.weight[j] = model->skin[i].weight[j]; @@ -4428,11 +4431,11 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size vrtx = (m3dvsave_t*)M3D_MALLOC(model->numvertex * sizeof(m3dvsave_t)); if(!vrtx) goto memerr; for(i = numvrtx = 0; i < model->numvertex; i++) { - if(vrtxidx[i] == (M3D_INDEX)-1U) continue; + if(vrtxidx[i] == M3D_UNDEF) continue; _m3d_round(quality, &model->vertex[i], &vertex.data); vertex.norm = norm ? norm[i] : 0; - if(vertex.data.skinid != (M3D_INDEX)-2U && !vertex.norm) { - vertex.data.skinid = vertex.data.skinid != (M3D_INDEX)-1U && skinidx ? skinidx[vertex.data.skinid] : (M3D_INDEX)-1U; + if(vertex.data.skinid != M3D_INDEXMAX && !vertex.norm) { + vertex.data.skinid = vertex.data.skinid != M3D_UNDEF && skinidx ? skinidx[vertex.data.skinid] : M3D_UNDEF; if(vertex.data.x > max_x) max_x = vertex.data.x; if(vertex.data.x < min_x) min_x = vertex.data.x; if(vertex.data.y > max_y) max_y = vertex.data.y; @@ -4481,7 +4484,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size if(scale == (M3D_FLOAT)0.0) scale = (M3D_FLOAT)1.0; if(scale != (M3D_FLOAT)1.0) { for(i = 0; i < numvrtx; i++) { - if(vrtx[i].data.skinid == (M3D_INDEX)-2U) continue; + if(vrtx[i].data.skinid == M3D_INDEXMAX) continue; vrtx[i].data.x /= scale; vrtx[i].data.y /= scale; vrtx[i].data.z /= scale; @@ -4528,7 +4531,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); ol = setlocale(LC_NUMERIC, NULL); setlocale(LC_NUMERIC, "C"); /* header */ - len = 64 + strlen(sn) + strlen(sl) + strlen(sa) + strlen(sd); + len = 64 + (unsigned int)(strlen(sn) + strlen(sl) + strlen(sa) + strlen(sd)); out = (unsigned char*)M3D_MALLOC(len); if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } ptr = (char*)out; @@ -4540,7 +4543,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); if(model->preview.data && model->preview.length) { sl = _m3d_safestr(sn, 0); if(sl) { - ptr -= (uintptr_t)out; len = (uintptr_t)ptr + 20; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)20); out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } ptr += sprintf(ptr, "Preview\r\n%s.png\r\n\r\n", sl); @@ -4550,11 +4553,11 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); M3D_FREE(sn); sn = NULL; /* texture map */ if(numtmap && tmap && !(flags & M3D_EXP_NOTXTCRD) && !(flags & M3D_EXP_NOFACE)) { - ptr -= (uintptr_t)out; len = (uintptr_t)ptr + maxtmap * 32 + 12; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(maxtmap * 32) + (uintptr_t)12); out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } ptr += sprintf(ptr, "Textmap\r\n"); - last = (M3D_INDEX)-1U; + last = M3D_UNDEF; for(i = 0; i < numtmap; i++) { if(tmap[i].newidx == last) continue; last = tmap[i].newidx; @@ -4564,11 +4567,11 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); } /* vertex chunk */ if(numvrtx && vrtx && !(flags & M3D_EXP_NOFACE)) { - ptr -= (uintptr_t)out; len = (uintptr_t)ptr + maxvrtx * 128 + 10; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(maxvrtx * 128) + (uintptr_t)10); out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } ptr += sprintf(ptr, "Vertex\r\n"); - last = (M3D_INDEX)-1U; + last = M3D_UNDEF; for(i = 0; i < numvrtx; i++) { if(vrtx[i].newidx == last) continue; last = vrtx[i].newidx; @@ -4579,7 +4582,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); if(skin[vrtx[i].data.skinid].data.weight[0] == (M3D_FLOAT)1.0) ptr += sprintf(ptr, " %d", skin[vrtx[i].data.skinid].data.boneid[0]); else - for(j = 0; j < M3D_NUMBONE && skin[vrtx[i].data.skinid].data.boneid[j] != (M3D_INDEX)-1U && + for(j = 0; j < M3D_NUMBONE && skin[vrtx[i].data.skinid].data.boneid[j] != M3D_UNDEF && skin[vrtx[i].data.skinid].data.weight[j] > (M3D_FLOAT)0.0; j++) ptr += sprintf(ptr, " %d:%g", skin[vrtx[i].data.skinid].data.boneid[j], skin[vrtx[i].data.skinid].data.weight[j]); @@ -4590,29 +4593,29 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); } /* bones chunk */ if(model->numbone && model->bone && !(flags & M3D_EXP_NOBONE)) { - ptr -= (uintptr_t)out; len = (uintptr_t)ptr + 9; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)9); for(i = 0; i < model->numbone; i++) { - len += strlen(model->bone[i].name) + 128; + len += (unsigned int)strlen(model->bone[i].name) + 128; } out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } ptr += sprintf(ptr, "Bones\r\n"); - ptr = _m3d_prtbone(ptr, model->bone, model->numbone, (M3D_INDEX)-1U, 0, vrtxidx); + ptr = _m3d_prtbone(ptr, model->bone, model->numbone, M3D_UNDEF, 0, vrtxidx); ptr += sprintf(ptr, "\r\n"); } /* materials */ if(model->nummaterial && !(flags & M3D_EXP_NOMATERIAL)) { for(j = 0; j < model->nummaterial; j++) { - if(mtrlidx[j] == (M3D_INDEX)-1U || !model->material[j].numprop || !model->material[j].prop) continue; + if(mtrlidx[j] == M3D_UNDEF || !model->material[j].numprop || !model->material[j].prop) continue; m = &model->material[j]; sn = _m3d_safestr(m->name, 0); if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } - ptr -= (uintptr_t)out; len = (uintptr_t)ptr + strlen(sn) + 12; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)12); for(i = 0; i < m->numprop; i++) { if(m->prop[i].type < 128) len += 32; else if(m->prop[i].value.textureid < model->numtexture && model->texture[m->prop[i].value.textureid].name) - len += strlen(model->texture[m->prop[i].value.textureid].name) + 16; + len += (unsigned int)strlen(model->texture[m->prop[i].value.textureid].name) + 16; } out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } @@ -4676,7 +4679,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); if(k) continue; sn = _m3d_safestr(model->inlined[j].name, 0); if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } - ptr -= (uintptr_t)out; len = (uintptr_t)ptr + strlen(sn) + 18; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)18); out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } ptr += sprintf(ptr, "Procedural\r\n%s\r\n\r\n", sn); @@ -4685,24 +4688,24 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); } /* mesh face */ if(model->numface && face && !(flags & M3D_EXP_NOFACE)) { - ptr -= (uintptr_t)out; len = (uintptr_t)ptr + model->numface * 128 + 6; - last = (M3D_INDEX)-1U; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(model->numface * 128) + (uintptr_t)6); + last = M3D_UNDEF; if(!(flags & M3D_EXP_NOMATERIAL)) for(i = 0; i < model->numface; i++) { - j = face[i].data.materialid < model->nummaterial ? face[i].data.materialid : (M3D_INDEX)-1U; + j = face[i].data.materialid < model->nummaterial ? face[i].data.materialid : M3D_UNDEF; if(j != last) { last = j; if(last < model->nummaterial) - len += strlen(model->material[last].name); + len += (unsigned int)strlen(model->material[last].name); len += 6; } } out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } ptr += sprintf(ptr, "Mesh\r\n"); - last = (M3D_INDEX)-1U; + last = M3D_UNDEF; for(i = 0; i < model->numface; i++) { - j = face[i].data.materialid < model->nummaterial ? face[i].data.materialid : (M3D_INDEX)-1U; + j = face[i].data.materialid < model->nummaterial ? face[i].data.materialid : M3D_UNDEF; if(!(flags & M3D_EXP_NOMATERIAL) && j != last) { last = j; if(last < model->nummaterial) { @@ -4716,14 +4719,14 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); /* hardcoded triangles. Should be repeated as many times as the number of edges in polygon */ for(j = 0; j < 3; j++) { ptr += sprintf(ptr, "%s%d", j?" ":"", vrtxidx[face[i].data.vertex[j]]); - k = -1U; - if(!(flags & M3D_EXP_NOTXTCRD) && (face[i].data.texcoord[j] != (M3D_INDEX)-1U) && - (tmapidx[face[i].data.texcoord[j]] != (M3D_INDEX)-1U)) { + k = M3D_NOTDEFINED; + if(!(flags & M3D_EXP_NOTXTCRD) && (face[i].data.texcoord[j] != M3D_UNDEF) && + (tmapidx[face[i].data.texcoord[j]] != M3D_UNDEF)) { k = tmapidx[face[i].data.texcoord[j]]; ptr += sprintf(ptr, "/%d", k); } - if(!(flags & M3D_EXP_NONORMAL) && (face[i].data.normal[j] != (M3D_INDEX)-1U)) - ptr += sprintf(ptr, "%s/%d", k == -1U? "/" : "", vrtxidx[face[i].data.normal[j]]); + if(!(flags & M3D_EXP_NONORMAL) && (face[i].data.normal[j] != M3D_UNDEF)) + ptr += sprintf(ptr, "%s/%d", k == M3D_NOTDEFINED? "/" : "", vrtxidx[face[i].data.normal[j]]); } ptr += sprintf(ptr, "\r\n"); } @@ -4734,22 +4737,22 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); for(j = 0; j < model->numshape; j++) { sn = _m3d_safestr(model->shape[j].name, 0); if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } - ptr -= (uintptr_t)out; len = (uintptr_t)ptr + strlen(sn) + 33; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)33); out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } ptr += sprintf(ptr, "Shape %s\r\n", sn); M3D_FREE(sn); sn = NULL; - if(model->shape[j].group != (M3D_INDEX)-1U && !(flags & M3D_EXP_NOBONE)) + if(model->shape[j].group != M3D_UNDEF && !(flags & M3D_EXP_NOBONE)) ptr += sprintf(ptr, "group %d\r\n", model->shape[j].group); for(i = 0; i < model->shape[j].numcmd; i++) { cmd = &model->shape[j].cmd[i]; if(cmd->type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])) || !cmd->arg) continue; cd = &m3d_commandtypes[cmd->type]; - ptr -= (uintptr_t)out; len = (uintptr_t)ptr + strlen(cd->key) + 3; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(cd->key) + (uintptr_t)3); for(k = 0; k < cd->p; k++) switch(cd->a[k]) { - case m3dcp_mi_t: if(cmd->arg[k] != -1U) { len += strlen(model->material[cmd->arg[k]].name) + 1; } break; + case m3dcp_mi_t: if(cmd->arg[k] != M3D_NOTDEFINED) { len += (unsigned int)strlen(model->material[cmd->arg[k]].name) + 1; } break; case m3dcp_va_t: len += cmd->arg[k] * (cd->p - k - 1) * 16; k = cd->p; break; default: len += 16; break; } @@ -4759,7 +4762,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); for(k = n = 0, l = cd->p; k < l; k++) { switch(cd->a[((k - n) % (cd->p - n)) + n]) { case m3dcp_mi_t: - if(cmd->arg[k] != -1U) { + if(cmd->arg[k] != M3D_NOTDEFINED) { sn = _m3d_safestr(model->material[cmd->arg[k]].name, 0); if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } ptr += sprintf(ptr, " %s", sn); @@ -4781,12 +4784,12 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); /* annotation labels */ if(model->numlabel && model->label && !(flags & M3D_EXP_NOFACE)) { for(i = 0, j = 3, length = NULL; i < model->numlabel; i++) { - if(model->label[i].name) j += strlen(model->label[i].name); - if(model->label[i].lang) j += strlen(model->label[i].lang); - if(model->label[i].text) j += strlen(model->label[i].text); + if(model->label[i].name) j += (unsigned int)strlen(model->label[i].name); + if(model->label[i].lang) j += (unsigned int)strlen(model->label[i].lang); + if(model->label[i].text) j += (unsigned int)strlen(model->label[i].text); j += 40; } - ptr -= (uintptr_t)out; len = (uintptr_t)ptr + j; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)j); out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } for(i = 0; i < model->numlabel; i++) { @@ -4821,7 +4824,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); a = &model->action[j]; sn = _m3d_safestr(a->name, 0); if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } - ptr -= (uintptr_t)out; len = (uintptr_t)ptr + strlen(sn) + 48; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)48); for(i = 0; i < a->numframe; i++) len += a->frame[i].numtransform * 128 + 8; out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; @@ -4842,9 +4845,9 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); if(model->numinlined && model->inlined) { for(i = j = 0; i < model->numinlined; i++) if(model->inlined[i].name) - j += strlen(model->inlined[i].name) + 6; + j += (unsigned int)strlen(model->inlined[i].name) + 6; if(j > 0) { - ptr -= (uintptr_t)out; len = (uintptr_t)ptr + j + 16; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)j + (uintptr_t)16); out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } ptr += sprintf(ptr, "Assets\r\n"); @@ -4858,7 +4861,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); if(model->numextra && (flags & M3D_EXP_EXTRA)) { for(i = 0; i < model->numextra; i++) { if(model->extra[i]->length < 9) continue; - ptr -= (uintptr_t)out; len = (uintptr_t)ptr + 17 + model->extra[i]->length * 3; + ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)17 + (uintptr_t)(model->extra[i]->length * 3)); out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } ptr += sprintf(ptr, "Extra %c%c%c%c\r\n", @@ -4873,7 +4876,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); } } setlocale(LC_NUMERIC, ol); - len = (uintptr_t)ptr - (uintptr_t)out; + len = (unsigned int)((uintptr_t)ptr - (uintptr_t)out); out = (unsigned char*)M3D_REALLOC(out, len + 1); if(!out) goto memerr; out[len] = 0; @@ -4889,10 +4892,10 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); memcpy((uint8_t*)h, "HEAD", 4); h->length = sizeof(m3dhdr_t); h->scale = scale; - i = strlen(sn); memcpy((uint8_t*)h + h->length, sn, i+1); h->length += i+1; M3D_FREE(sn); - i = strlen(sl); memcpy((uint8_t*)h + h->length, sl, i+1); h->length += i+1; M3D_FREE(sl); - i = strlen(sa); memcpy((uint8_t*)h + h->length, sa, i+1); h->length += i+1; M3D_FREE(sa); - i = strlen(sd); memcpy((uint8_t*)h + h->length, sd, i+1); h->length += i+1; M3D_FREE(sd); + i = (unsigned int)strlen(sn); memcpy((uint8_t*)h + h->length, sn, i+1); h->length += i+1; M3D_FREE(sn); + i = (unsigned int)strlen(sl); memcpy((uint8_t*)h + h->length, sl, i+1); h->length += i+1; M3D_FREE(sl); + i = (unsigned int)strlen(sa); memcpy((uint8_t*)h + h->length, sa, i+1); h->length += i+1; M3D_FREE(sa); + i = (unsigned int)strlen(sd); memcpy((uint8_t*)h + h->length, sd, i+1); h->length += i+1; M3D_FREE(sd); sn = sl = sa = sd = NULL; if(model->inlined) for(i = 0; i < model->numinlined; i++) { @@ -4960,7 +4963,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); memcpy((uint8_t*)h + len, "TMAP", 4); length = (uint32_t*)((uint8_t*)h + len + 4); out = (uint8_t*)h + len + 8; - last = (M3D_INDEX)-1U; + last = M3D_UNDEF; for(i = 0; i < numtmap; i++) { if(tmap[i].newidx == last) continue; last = tmap[i].newidx; @@ -4974,7 +4977,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); case 8: *((double*)out) = tmap[i].data.u; out += 8; *((double*)out) = tmap[i].data.v; out += 8; break; } } - *length = (uintptr_t)out - (uintptr_t)((uint8_t*)h + len); + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); out = NULL; len += *length; } @@ -4986,7 +4989,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); memcpy((uint8_t*)h + len, "VRTS", 4); length = (uint32_t*)((uint8_t*)h + len + 4); out = (uint8_t*)h + len + 8; - last = (M3D_INDEX)-1U; + last = M3D_UNDEF; for(i = 0; i < numvrtx; i++) { if(vrtx[i].newidx == last) continue; last = vrtx[i].newidx; @@ -5024,7 +5027,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); } out = _m3d_addidx(out, sk_s, vrtx[i].data.skinid); } - *length = (uintptr_t)out - (uintptr_t)((uint8_t*)h + len); + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); out = NULL; len += *length; } @@ -5046,12 +5049,12 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); out = _m3d_addidx(out, vi_s, vrtxidx[model->bone[i].ori]); } if(numskin && skin && sk_s) { - last = (M3D_INDEX)-1U; + last = M3D_UNDEF; for(i = 0; i < numskin; i++) { if(skin[i].newidx == last) continue; last = skin[i].newidx; memset(&weights, 0, nb_s); - for(j = 0; j < (uint32_t)nb_s && skin[i].data.boneid[j] != (M3D_INDEX)-1U && + for(j = 0; j < (uint32_t)nb_s && skin[i].data.boneid[j] != M3D_UNDEF && skin[i].data.weight[j] > (M3D_FLOAT)0.0; j++) weights[j] = (uint8_t)(skin[i].data.weight[j] * 255); switch(nb_s) { @@ -5060,20 +5063,20 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); case 4: *((uint32_t*)out) = *((uint32_t*)&weights[0]); out += 4; break; case 8: *((uint64_t*)out) = *((uint64_t*)&weights[0]); out += 8; break; } - for(j = 0; j < (uint32_t)nb_s && skin[i].data.boneid[j] != (M3D_INDEX)-1U && weights[j]; j++) { + for(j = 0; j < (uint32_t)nb_s && skin[i].data.boneid[j] != M3D_UNDEF && weights[j]; j++) { out = _m3d_addidx(out, bi_s, skin[i].data.boneid[j]); *length += bi_s; } } } - *length = (uintptr_t)out - (uintptr_t)((uint8_t*)h + len); + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); out = NULL; len += *length; } /* materials */ if(model->nummaterial && !(flags & M3D_EXP_NOMATERIAL)) { for(j = 0; j < model->nummaterial; j++) { - if(mtrlidx[j] == (M3D_INDEX)-1U || !model->material[j].numprop || !model->material[j].prop) continue; + if(mtrlidx[j] == M3D_UNDEF || !model->material[j].numprop || !model->material[j].prop) continue; m = &model->material[j]; chunklen = 12 + si_s + m->numprop * 5; h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); @@ -5115,7 +5118,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); break; } } - *length = (uintptr_t)out - (uintptr_t)((uint8_t*)h + len); + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); len += *length; out = NULL; } @@ -5152,7 +5155,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); memcpy((uint8_t*)h + len, "MESH", 4); length = (uint32_t*)((uint8_t*)h + len + 4); out = (uint8_t*)h + len + 8; - last = (M3D_INDEX)-1U; + last = M3D_UNDEF; for(i = 0; i < model->numface; i++) { if(!(flags & M3D_EXP_NOMATERIAL) && face[i].data.materialid != last) { last = face[i].data.materialid; @@ -5162,10 +5165,10 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); } /* hardcoded triangles. */ k = (3 << 4) | - (((flags & M3D_EXP_NOTXTCRD) || !ti_s || face[i].data.texcoord[0] == (M3D_INDEX)-1U || - face[i].data.texcoord[1] == (M3D_INDEX)-1U || face[i].data.texcoord[2] == (M3D_INDEX)-1U) ? 0 : 1) | - (((flags & M3D_EXP_NONORMAL) || face[i].data.normal[0] == (M3D_INDEX)-1U || - face[i].data.normal[1] == (M3D_INDEX)-1U || face[i].data.normal[2] == (M3D_INDEX)-1U) ? 0 : 2); + (((flags & M3D_EXP_NOTXTCRD) || !ti_s || face[i].data.texcoord[0] == M3D_UNDEF || + face[i].data.texcoord[1] == M3D_UNDEF || face[i].data.texcoord[2] == M3D_UNDEF) ? 0 : 1) | + (((flags & M3D_EXP_NONORMAL) || face[i].data.normal[0] == M3D_UNDEF || + face[i].data.normal[1] == M3D_UNDEF || face[i].data.normal[2] == M3D_UNDEF) ? 0 : 2); *out++ = k; for(j = 0; j < 3; j++) { out = _m3d_addidx(out, vi_s, vrtxidx[face[i].data.vertex[j]]); @@ -5175,7 +5178,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); out = _m3d_addidx(out, vi_s, vrtxidx[face[i].data.normal[j]]); } } - *length = (uintptr_t)out - (uintptr_t)((uint8_t*)h + len); + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); len += *length; out = NULL; } @@ -5226,7 +5229,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); } } } - *length = (uintptr_t)out - (uintptr_t)((uint8_t*)h + len); + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); len += *length; out = NULL; } @@ -5238,7 +5241,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); sl = model->label[i].lang; sn = model->label[i].name; if(length) { - *length = (uintptr_t)out - (uintptr_t)((uint8_t*)h + len); + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); len += *length; } chunklen = 8 + 2 * si_s + ci_s + model->numlabel * (vi_s + si_s); @@ -5260,7 +5263,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->label[l].text)); } if(length) { - *length = (uintptr_t)out - (uintptr_t)((uint8_t*)h + len); + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); len += *length; } out = NULL; @@ -5288,7 +5291,7 @@ memerr: if(vrtxidx) M3D_FREE(vrtxidx); out = _m3d_addidx(out, vi_s, vrtxidx[a->frame[i].transform[k].ori]); } } - *length = (uintptr_t)out - (uintptr_t)((uint8_t*)h + len); + *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); len += *length; out = NULL; } From fb95e4982453f276c8a1abc69b2088793a65fbde Mon Sep 17 00:00:00 2001 From: bzt Date: Wed, 11 Dec 2019 05:47:59 +0100 Subject: [PATCH 08/36] More MSVC workarounds --- code/M3D/m3d.h | 57 ++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/code/M3D/m3d.h b/code/M3D/m3d.h index d1fedc5b5..1b79fb785 100644 --- a/code/M3D/m3d.h +++ b/code/M3D/m3d.h @@ -91,7 +91,7 @@ typedef uint16_t M3D_INDEX; #else #define _inline #define _pack -#define _unused +#define _unused __pragma(warning(suppress:4100)) #endif #ifndef __cplusplus #define _register register @@ -2202,6 +2202,9 @@ void _m3d_getpr(m3d_t *model, _unused m3dread_t readfilecb, _unused m3dfree_t f } if(freecb && buff) (*freecb)(buff); #else + (void)readfilecb; + (void)freecb; + (void)fn; M3D_LOG("Unimplemented interpreter"); M3D_LOG(fn); model->errcode = M3D_ERR_UNIMPL; @@ -2294,7 +2297,7 @@ void _m3d_mat(M3D_FLOAT *r, m3dv_t *p, m3dv_t *q) } #endif #if !defined(M3D_NOANIMATION) || !defined(M3D_NONORMALS) -/* fast inverse square root calculation. returns 1/sqrt(x) */ +/* portable fast inverse square root calculation. returns 1/sqrt(x) */ static M3D_FLOAT _m3d_rsq(M3D_FLOAT x) { #ifdef M3D_DOUBLE @@ -3053,12 +3056,12 @@ memerr: M3D_LOG("Out of memory"); for(i = 0, data += sizeof(m3dchunk_t); data < chunk; i++) { switch(model->vc_s) { case 1: - model->tmap[i].u = (M3D_FLOAT)(data[0]) / 255; - model->tmap[i].v = (M3D_FLOAT)(data[1]) / 255; + model->tmap[i].u = (M3D_FLOAT)(data[0]) / (M3D_FLOAT)255.0; + model->tmap[i].v = (M3D_FLOAT)(data[1]) / (M3D_FLOAT)255.0; break; case 2: - model->tmap[i].u = (M3D_FLOAT)(*((int16_t*)(data+0))) / 65535; - model->tmap[i].v = (M3D_FLOAT)(*((int16_t*)(data+2))) / 65535; + model->tmap[i].u = (M3D_FLOAT)(*((int16_t*)(data+0))) / (M3D_FLOAT)65535.0; + model->tmap[i].v = (M3D_FLOAT)(*((int16_t*)(data+2))) / (M3D_FLOAT)65535.0; break; case 4: model->tmap[i].u = (M3D_FLOAT)(*((float*)(data+0))); @@ -3085,17 +3088,17 @@ memerr: M3D_LOG("Out of memory"); for(i = 0, data += sizeof(m3dchunk_t); data < chunk && i < model->numvertex; i++) { switch(model->vc_s) { case 1: - model->vertex[i].x = (M3D_FLOAT)((int8_t)data[0]) / 127; - model->vertex[i].y = (M3D_FLOAT)((int8_t)data[1]) / 127; - model->vertex[i].z = (M3D_FLOAT)((int8_t)data[2]) / 127; - model->vertex[i].w = (M3D_FLOAT)((int8_t)data[3]) / 127; + model->vertex[i].x = (M3D_FLOAT)((int8_t)data[0]) / (M3D_FLOAT)127.0; + model->vertex[i].y = (M3D_FLOAT)((int8_t)data[1]) / (M3D_FLOAT)127.0; + model->vertex[i].z = (M3D_FLOAT)((int8_t)data[2]) / (M3D_FLOAT)127.0; + model->vertex[i].w = (M3D_FLOAT)((int8_t)data[3]) / (M3D_FLOAT)127.0; data += 4; break; case 2: - model->vertex[i].x = (M3D_FLOAT)(*((int16_t*)(data+0))) / 32767; - model->vertex[i].y = (M3D_FLOAT)(*((int16_t*)(data+2))) / 32767; - model->vertex[i].z = (M3D_FLOAT)(*((int16_t*)(data+4))) / 32767; - model->vertex[i].w = (M3D_FLOAT)(*((int16_t*)(data+6))) / 32767; + model->vertex[i].x = (M3D_FLOAT)(*((int16_t*)(data+0))) / (M3D_FLOAT)32767.0; + model->vertex[i].y = (M3D_FLOAT)(*((int16_t*)(data+2))) / (M3D_FLOAT)32767.0; + model->vertex[i].z = (M3D_FLOAT)(*((int16_t*)(data+4))) / (M3D_FLOAT)32767.0; + model->vertex[i].w = (M3D_FLOAT)(*((int16_t*)(data+6))) / (M3D_FLOAT)32767.0; data += 8; break; case 4: @@ -3167,7 +3170,7 @@ memerr: M3D_LOG("Out of memory"); if(j >= M3D_NUMBONE) data += model->bi_s; else { - model->skin[i].weight[j] = (M3D_FLOAT)(weights[j]) / 255; + model->skin[i].weight[j] = (M3D_FLOAT)(weights[j]) / (M3D_FLOAT)255.0; w += model->skin[i].weight[j]; data = _m3d_getidx(data, model->bi_s, &model->skin[i].boneid[j]); } @@ -4036,16 +4039,16 @@ static void _m3d_round(int quality, m3dv_t *src, m3dv_t *dst) /* round according to quality */ switch(quality) { case M3D_EXP_INT8: - t = src->x * 127 + (src->x >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->x = (M3D_FLOAT)t / (M3D_FLOAT)127.0; - t = src->y * 127 + (src->y >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->y = (M3D_FLOAT)t / (M3D_FLOAT)127.0; - t = src->z * 127 + (src->z >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->z = (M3D_FLOAT)t / (M3D_FLOAT)127.0; - t = src->w * 127 + (src->w >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->w = (M3D_FLOAT)t / (M3D_FLOAT)127.0; + t = (int)(src->x * 127 + (src->x >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->x = (M3D_FLOAT)t / (M3D_FLOAT)127.0; + t = (int)(src->y * 127 + (src->y >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->y = (M3D_FLOAT)t / (M3D_FLOAT)127.0; + t = (int)(src->z * 127 + (src->z >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->z = (M3D_FLOAT)t / (M3D_FLOAT)127.0; + t = (int)(src->w * 127 + (src->w >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->w = (M3D_FLOAT)t / (M3D_FLOAT)127.0; break; case M3D_EXP_INT16: - t = src->x * 32767 + (src->x >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->x = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; - t = src->y * 32767 + (src->y >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->y = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; - t = src->z * 32767 + (src->z >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->z = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; - t = src->w * 32767 + (src->w >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5); dst->w = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; + t = (int)(src->x * 32767 + (src->x >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->x = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; + t = (int)(src->y * 32767 + (src->y >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->y = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; + t = (int)(src->z * 32767 + (src->z >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->z = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; + t = (int)(src->w * 32767 + (src->w >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->w = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; break; } if(dst->x == (M3D_FLOAT)-0.0) dst->x = (M3D_FLOAT)0.0; @@ -4351,12 +4354,12 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size if(tmapidx[i] == M3D_UNDEF) continue; switch(quality) { case M3D_EXP_INT8: - l = model->tmap[i].u * 255; tcoord.data.u = (M3D_FLOAT)l / (M3D_FLOAT)255.0; - l = model->tmap[i].v * 255; tcoord.data.v = (M3D_FLOAT)l / (M3D_FLOAT)255.0; + l = (unsigned int)(model->tmap[i].u * 255); tcoord.data.u = (M3D_FLOAT)l / (M3D_FLOAT)255.0; + l = (unsigned int)(model->tmap[i].v * 255); tcoord.data.v = (M3D_FLOAT)l / (M3D_FLOAT)255.0; break; case M3D_EXP_INT16: - l = model->tmap[i].u * 65535; tcoord.data.u = (M3D_FLOAT)l / (M3D_FLOAT)65535.0; - l = model->tmap[i].v * 65535; tcoord.data.v = (M3D_FLOAT)l / (M3D_FLOAT)65535.0; + l = (unsigned int)(model->tmap[i].u * 65535); tcoord.data.u = (M3D_FLOAT)l / (M3D_FLOAT)65535.0; + l = (unsigned int)(model->tmap[i].v * 65535); tcoord.data.v = (M3D_FLOAT)l / (M3D_FLOAT)65535.0; break; default: tcoord.data.u = model->tmap[i].u; From e17431b993ec946587af4727a68edb617bf16653 Mon Sep 17 00:00:00 2001 From: bzt Date: Wed, 11 Dec 2019 06:21:35 +0100 Subject: [PATCH 09/36] Added extra check for file size --- code/M3D/M3DImporter.cpp | 4 ++++ code/M3D/M3DWrapper.cpp | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/code/M3D/M3DImporter.cpp b/code/M3D/M3DImporter.cpp index 6627d1f74..a183f8bda 100644 --- a/code/M3D/M3DImporter.cpp +++ b/code/M3D/M3DImporter.cpp @@ -159,6 +159,10 @@ void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSys if (fileSize != pStream->Read(buffer.data(), 1, fileSize)) { throw DeadlyImportError("Failed to read the file " + file + "."); } + // extra check for binary format's first 8 bytes. Not done for the ASCII variant + if(!memcmp(buffer.data(), "3DMO", 4) && memcmp(buffer.data() + 4, &fileSize, 4)) { + throw DeadlyImportError("Bad binary header in file " + file + "."); + } // Get the path for external assets std::string folderName("./"); diff --git a/code/M3D/M3DWrapper.cpp b/code/M3D/M3DWrapper.cpp index 0060c894e..d8fba4839 100644 --- a/code/M3D/M3DWrapper.cpp +++ b/code/M3D/M3DWrapper.cpp @@ -75,7 +75,8 @@ unsigned char *m3dimporter_readfile(char *fn, unsigned int *size) { (reinterpret_cast(m3dimporter_pIOHandler))->Open(file, "rb")); size_t fileSize = 0; unsigned char *data = NULL; - // sometimes pStream is nullptr for some reason (should be an empty object returning nothing I guess) + // sometimes pStream is nullptr in a single-threaded scenario too for some reason + // (should be an empty object returning nothing I guess) if (pStream) { fileSize = pStream->FileSize(); // should be allocated with malloc(), because the library will call free() to deallocate @@ -101,7 +102,7 @@ M3DWrapper::M3DWrapper() { M3DWrapper::M3DWrapper(IOSystem *pIOHandler, const std::vector &buffer) { #if AI_M3D_USE_STDMUTEX - // M3D is NOT thread-safe, so lock the global mutex + // M3D is thread-safe, but pIOHandler is NOT, so lock the global mutex const std::lock_guard lock(file_mutex); #endif // pass this IOHandler to the C callback From 30634ff56e4f9cfcab3af5fa7a523be7c2a6bca1 Mon Sep 17 00:00:00 2001 From: bzt Date: Fri, 13 Dec 2019 06:38:49 +0100 Subject: [PATCH 10/36] Be more tolerant to invalid input --- code/M3D/m3d.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/M3D/m3d.h b/code/M3D/m3d.h index 1b79fb785..25baacc24 100644 --- a/code/M3D/m3d.h +++ b/code/M3D/m3d.h @@ -2994,11 +2994,11 @@ asciiend: while(buff < end && !M3D_CHUNKMAGIC(buff, 'O','M','D','3')) { data = buff; len = ((m3dchunk_t*)data)->length; - if(len < sizeof(m3dchunk_t)) { + buff += len; + if(len < sizeof(m3dchunk_t) || buff >= end) { M3D_LOG("Invalid chunk size"); break; } - buff += len; len -= sizeof(m3dchunk_t) + model->si_s; /* inlined assets */ @@ -3024,11 +3024,11 @@ memerr: M3D_LOG("Out of memory"); while(chunk < end && !M3D_CHUNKMAGIC(chunk, 'O','M','D','3')) { data = chunk; len = ((m3dchunk_t*)chunk)->length; - if(len < sizeof(m3dchunk_t)) { + chunk += len; + if(len < sizeof(m3dchunk_t) || chunk >= end) { M3D_LOG("Invalid chunk size"); break; } - chunk += len; len -= sizeof(m3dchunk_t); /* preview chunk */ From 6b2fe41a3bdfe9f124caa964385c7047ae619f74 Mon Sep 17 00:00:00 2001 From: bzt Date: Fri, 13 Dec 2019 07:30:59 +0100 Subject: [PATCH 11/36] Replaced mutex with thread-local variable --- code/M3D/M3DWrapper.cpp | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/code/M3D/M3DWrapper.cpp b/code/M3D/M3DWrapper.cpp index d8fba4839..46f3278cb 100644 --- a/code/M3D/M3DWrapper.cpp +++ b/code/M3D/M3DWrapper.cpp @@ -48,24 +48,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#ifndef AI_M3D_USE_STDMUTEX -#if (__cplusplus >= 201103L) || (_MSC_VER >= 1900) // C++11 and MSVC 2015 onwards -#define AI_M3D_USE_STDMUTEX 1 -#else -#define AI_M3D_USE_STDMUTEX 0 -#endif -#endif - -#if AI_M3D_USE_STDMUTEX -#include -std::mutex file_mutex; -#endif - // workaround: the M3D SDK expects a C callback, but we want to use Assimp::IOSystem to implement that -// This makes it non-rentrant so lock a mutex (requires C++11) extern "C" { -void *m3dimporter_pIOHandler; +thread_local void *m3dimporter_pIOHandler; unsigned char *m3dimporter_readfile(char *fn, unsigned int *size) { ai_assert(nullptr != fn); @@ -101,11 +87,7 @@ M3DWrapper::M3DWrapper() { } M3DWrapper::M3DWrapper(IOSystem *pIOHandler, const std::vector &buffer) { -#if AI_M3D_USE_STDMUTEX - // M3D is thread-safe, but pIOHandler is NOT, so lock the global mutex - const std::lock_guard lock(file_mutex); -#endif - // pass this IOHandler to the C callback + // pass this IOHandler to the C callback in a thread-local pointer m3dimporter_pIOHandler = pIOHandler; m3d_ = m3d_load(const_cast(buffer.data()), m3dimporter_readfile, free, nullptr); // Clear the C callback From 32835873f0bb34d440a07b2339fbb3e9f9e37faa Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 13 Dec 2019 08:19:39 +0100 Subject: [PATCH 12/36] Update m3d.h Fix review warning. --- code/M3D/m3d.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/M3D/m3d.h b/code/M3D/m3d.h index 25baacc24..5d78374f0 100644 --- a/code/M3D/m3d.h +++ b/code/M3D/m3d.h @@ -87,7 +87,11 @@ typedef uint16_t M3D_INDEX; #ifndef _MSC_VER #define _inline __inline__ #define _pack __attribute__((packed)) -#define _unused __attribute__((unused)) +# ifdef __cplusplus // only for c++ code +# define _unused __attribute__((unused)) +# else // or c code just use void +# define _unused (void) +# endif #else #define _inline #define _pack From 8be1060ec4eed9b498109e5cb922215e63c20f2f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 13 Dec 2019 08:21:45 +0100 Subject: [PATCH 13/36] Update m3d.h Fix vs-defines for unused define as well. --- code/M3D/m3d.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/M3D/m3d.h b/code/M3D/m3d.h index 5d78374f0..a36cc9613 100644 --- a/code/M3D/m3d.h +++ b/code/M3D/m3d.h @@ -95,7 +95,11 @@ typedef uint16_t M3D_INDEX; #else #define _inline #define _pack -#define _unused __pragma(warning(suppress:4100)) +# ifdef __cplusplus // only for c++ code +# define _unused __pragma(warning(suppress:4100)) +# else // or c code just use void +# define _unused (void) +# endif // ___cplusplus #endif #ifndef __cplusplus #define _register register From f151a5d11db0330cbbc7fbde3b4b76c328357b25 Mon Sep 17 00:00:00 2001 From: bzt Date: Fri, 13 Dec 2019 09:04:40 +0100 Subject: [PATCH 14/36] MSVC2013 alternative to thread_local --- code/M3D/M3DWrapper.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/code/M3D/M3DWrapper.cpp b/code/M3D/M3DWrapper.cpp index 46f3278cb..c0de1cf31 100644 --- a/code/M3D/M3DWrapper.cpp +++ b/code/M3D/M3DWrapper.cpp @@ -48,10 +48,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -// workaround: the M3D SDK expects a C callback, but we want to use Assimp::IOSystem to implement that +#if (__cplusplus >= 201103L) || !defined(_MSC_VER) || (_MSC_VER >= 1900) // C++11 and MSVC 2015 onwards +# define threadlocal thread_local +#else +# if defined(_MSC_VER) && (_MSC_VER >= 1800) // there's an alternative for MSVC 2013 +# define threadlocal __declspec(thread) +# else +# define threadlocal +# endif +#endif extern "C" { -thread_local void *m3dimporter_pIOHandler; + +// workaround: the M3D SDK expects a C callback, but we want to use Assimp::IOSystem to implement that +threadlocal void *m3dimporter_pIOHandler; unsigned char *m3dimporter_readfile(char *fn, unsigned int *size) { ai_assert(nullptr != fn); From 512e6dff4fd3ba597e90f2d727813c522561d723 Mon Sep 17 00:00:00 2001 From: bzt Date: Fri, 13 Dec 2019 09:16:41 +0100 Subject: [PATCH 15/36] Fixed ANSI C compiler issue --- code/M3D/m3d.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/code/M3D/m3d.h b/code/M3D/m3d.h index a36cc9613..5ab9f14c0 100644 --- a/code/M3D/m3d.h +++ b/code/M3D/m3d.h @@ -87,19 +87,15 @@ typedef uint16_t M3D_INDEX; #ifndef _MSC_VER #define _inline __inline__ #define _pack __attribute__((packed)) -# ifdef __cplusplus // only for c++ code -# define _unused __attribute__((unused)) -# else // or c code just use void -# define _unused (void) -# endif +#define _unused __attribute__((unused)) #else #define _inline #define _pack -# ifdef __cplusplus // only for c++ code +# ifdef __cplusplus /* only for c++ code */ # define _unused __pragma(warning(suppress:4100)) -# else // or c code just use void +# else # define _unused (void) -# endif // ___cplusplus +# endif #endif #ifndef __cplusplus #define _register register From a10b0d4de3061e11d430898f0d7a14a2ee18246e Mon Sep 17 00:00:00 2001 From: bzt Date: Sat, 14 Dec 2019 08:52:48 +0100 Subject: [PATCH 16/36] Requested modifications --- code/M3D/M3DExporter.cpp | 35 ++++++++++++++--- code/M3D/M3DImporter.cpp | 82 ++++++++++++++++++++++++++-------------- code/M3D/M3DWrapper.cpp | 21 ++++++---- code/M3D/M3DWrapper.h | 4 ++ code/M3D/m3d.h | 57 ++++++++++++++-------------- 5 files changed, 130 insertions(+), 69 deletions(-) diff --git a/code/M3D/M3DExporter.cpp b/code/M3D/M3DExporter.cpp index faf5a9585..a3988db0b 100644 --- a/code/M3D/M3DExporter.cpp +++ b/code/M3D/M3DExporter.cpp @@ -45,7 +45,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define M3D_IMPLEMENTATION #define M3D_NOIMPORTER #define M3D_EXPORTER -#define M3D_ASCII #ifndef ASSIMP_BUILD_NO_M3D_IMPORTER #define M3D_NODUP #endif @@ -65,9 +64,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include "M3DWrapper.h" #include "M3DExporter.h" #include "M3DMaterials.h" -#include "M3DWrapper.h" // RESOURCES: // https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md @@ -131,6 +130,28 @@ void addProp(m3dm_t *m, uint8_t type, uint32_t value) { m->prop[i].value.num = value; } +// ------------------------------------------------------------------------------------------------ +// convert aiString to identifier safe C string. This is a duplication of _m3d_safestr +char *SafeStr(aiString str, bool isStrict) +{ + char *s = (char *)&str.data; + char *d, *ret; + int i, len; + + for(len = str.length + 1; *s && (*s == ' ' || *s == '\t'); s++, len--); + if(len > 255) len = 255; + ret = (char *)M3D_MALLOC(len + 1); + if (!ret) { + throw DeadlyExportError("memory allocation error"); + } + for(i = 0, d = ret; i < len && *s && *s != '\r' && *s != '\n'; s++, d++, i++) { + *d = isStrict && (*s == ' ' || *s == '\t' || *s == '/' || *s == '\\') ? '_' : (*s == '\t' ? ' ' : *s); + } + for(; d > ret && (*(d-1) == ' ' || *(d-1) == '\t'); d--); + *d = 0; + return ret; +} + // ------------------------------------------------------------------------------------------------ // add a material to the output M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) { @@ -157,7 +178,7 @@ M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) { if (!m3d->material) { throw DeadlyExportError("memory allocation error"); } - m3d->material[mi].name = _m3d_safestr((char *)&name.data, 0); + m3d->material[mi].name = SafeStr(name, true); m3d->material[mi].numprop = 0; m3d->material[mi].prop = NULL; // iterate through the material property table and see what we got @@ -218,7 +239,7 @@ M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) { (name.data[j + 1] == 'g' || name.data[j + 1] == 'G')) name.data[j] = 0; // do we have this texture saved already? - fn = _m3d_safestr((char *)&name.data, 0); + fn = SafeStr(name, true); for (j = 0, i = M3D_NOTDEFINED; j < m3d->numtexture; j++) if (!strcmp(fn, m3d->texture[j].name)) { i = j; @@ -275,11 +296,15 @@ void ExportSceneA3D( const ExportProperties *pProperties ) { +#ifdef M3D_ASCII // initialize the exporter M3DExporter exporter(pScene, pProperties); // perform ascii export exporter.doExport(pFile, pIOSystem, true); +#else + throw DeadlyExportError("Assimp configured without M3D_ASCII support"); +#endif } // ------------------------------------------------------------------------------------------------ @@ -306,7 +331,7 @@ void M3DExporter::doExport( if (!m3d) { throw DeadlyExportError("memory allocation error"); } - m3d->name = _m3d_safestr((char *)&mScene->mRootNode->mName.data, 2); + m3d->name = SafeStr(mScene->mRootNode->mName, false); // Create a model from assimp structures aiMatrix4x4 m; diff --git a/code/M3D/M3DImporter.cpp b/code/M3D/M3DImporter.cpp index a183f8bda..a77e75a27 100644 --- a/code/M3D/M3DImporter.cpp +++ b/code/M3D/M3DImporter.cpp @@ -43,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_M3D_IMPORTER #define M3D_IMPLEMENTATION -#define M3D_ASCII #define M3D_NONORMALS /* leave the post-processing to Assimp */ #define M3D_NOWEIGHTS #define M3D_NOANIMATION @@ -57,9 +56,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include "M3DWrapper.h" #include "M3DImporter.h" #include "M3DMaterials.h" -#include "M3DWrapper.h" // RESOURCES: // https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md @@ -96,7 +95,11 @@ static const aiImporterDesc desc = { 0, 0, 0, +#ifdef M3D_ASCII "m3d a3d" +#else + "m3d" +#endif }; namespace Assimp { @@ -113,7 +116,11 @@ M3DImporter::M3DImporter() : bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { const std::string extension = GetExtension(pFile); - if (extension == "m3d" || extension == "a3d") + if (extension == "m3d" +#ifdef M3D_ASCII + || extension == "a3d" +#endif + ) return true; else if (!extension.length() || checkSig) { if (!pIOHandler) { @@ -131,7 +138,11 @@ bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c if (4 != pStream->Read(data, 1, 4)) { return false; } - return !memcmp(data, "3DMO", 4) /* bin */ || !memcmp(data, "3dmo", 4) /* ASCII */; + return !memcmp(data, "3DMO", 4) /* bin */ +#ifdef M3D_ASCII + || !memcmp(data, "3dmo", 4) /* ASCII */ +#endif + ; } return false; } @@ -163,6 +174,12 @@ void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSys if(!memcmp(buffer.data(), "3DMO", 4) && memcmp(buffer.data() + 4, &fileSize, 4)) { throw DeadlyImportError("Bad binary header in file " + file + "."); } +#ifdef M3D_ASCII + // make sure there's a terminator zero character, as input must be ASCIIZ + if(!memcmp(buffer.data(), "3dmo", 4)) { + buffer.push_back(0); + } +#endif // Get the path for external assets std::string folderName("./"); @@ -180,7 +197,6 @@ void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSys // let the C SDK do the hard work for us M3DWrapper m3d(pIOHandler, buffer); - if (!m3d) { throw DeadlyImportError("Unable to parse " + file + " as M3D."); } @@ -310,32 +326,40 @@ void M3DImporter::importTextures(const M3DWrapper &m3d) { for (i = 0; i < m3d->numtexture; i++) { unsigned int j, k; t = &m3d->texture[i]; - if (!t->w || !t->h || !t->f || !t->d) continue; aiTexture *tx = new aiTexture; - strcpy(tx->achFormatHint, formatHint[t->f - 1]); tx->mFilename = aiString(std::string(t->name) + ".png"); - tx->mWidth = t->w; - tx->mHeight = t->h; - tx->pcData = new aiTexel[tx->mWidth * tx->mHeight]; - for (j = k = 0; j < tx->mWidth * tx->mHeight; j++) { - switch (t->f) { - case 1: tx->pcData[j].g = t->d[k++]; break; - case 2: - tx->pcData[j].g = t->d[k++]; - tx->pcData[j].a = t->d[k++]; - break; - case 3: - tx->pcData[j].r = t->d[k++]; - tx->pcData[j].g = t->d[k++]; - tx->pcData[j].b = t->d[k++]; - tx->pcData[j].a = 255; - break; - case 4: - tx->pcData[j].r = t->d[k++]; - tx->pcData[j].g = t->d[k++]; - tx->pcData[j].b = t->d[k++]; - tx->pcData[j].a = t->d[k++]; - break; + if (!t->w || !t->h || !t->f || !t->d) { + /* without ASSIMP_USE_M3D_READFILECB, we only have the filename, but no texture data ever */ + tx->mWidth = 0; + tx->mHeight = 0; + memcpy(tx->achFormatHint, "png\000", 4); + tx->pcData = nullptr; + } else { + /* if we have the texture loaded, set format hint and pcData too */ + tx->mWidth = t->w; + tx->mHeight = t->h; + strcpy(tx->achFormatHint, formatHint[t->f - 1]); + tx->pcData = new aiTexel[tx->mWidth * tx->mHeight]; + for (j = k = 0; j < tx->mWidth * tx->mHeight; j++) { + switch (t->f) { + case 1: tx->pcData[j].g = t->d[k++]; break; + case 2: + tx->pcData[j].g = t->d[k++]; + tx->pcData[j].a = t->d[k++]; + break; + case 3: + tx->pcData[j].r = t->d[k++]; + tx->pcData[j].g = t->d[k++]; + tx->pcData[j].b = t->d[k++]; + tx->pcData[j].a = 255; + break; + case 4: + tx->pcData[j].r = t->d[k++]; + tx->pcData[j].g = t->d[k++]; + tx->pcData[j].b = t->d[k++]; + tx->pcData[j].a = t->d[k++]; + break; + } } } mScene->mTextures[i] = tx; diff --git a/code/M3D/M3DWrapper.cpp b/code/M3D/M3DWrapper.cpp index c0de1cf31..28eda845f 100644 --- a/code/M3D/M3DWrapper.cpp +++ b/code/M3D/M3DWrapper.cpp @@ -48,15 +48,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#if (__cplusplus >= 201103L) || !defined(_MSC_VER) || (_MSC_VER >= 1900) // C++11 and MSVC 2015 onwards -# define threadlocal thread_local -#else -# if defined(_MSC_VER) && (_MSC_VER >= 1800) // there's an alternative for MSVC 2013 -# define threadlocal __declspec(thread) +#ifdef ASSIMP_USE_M3D_READFILECB + +# if (__cplusplus >= 201103L) || !defined(_MSC_VER) || (_MSC_VER >= 1900) // C++11 and MSVC 2015 onwards +# define threadlocal thread_local # else -# define threadlocal +# if defined(_MSC_VER) && (_MSC_VER >= 1800) // there's an alternative for MSVC 2013 +# define threadlocal __declspec(thread) +# else +# define threadlocal +# endif # endif -#endif extern "C" { @@ -89,6 +91,7 @@ unsigned char *m3dimporter_readfile(char *fn, unsigned int *size) { return data; } } +#endif namespace Assimp { M3DWrapper::M3DWrapper() { @@ -97,11 +100,15 @@ M3DWrapper::M3DWrapper() { } M3DWrapper::M3DWrapper(IOSystem *pIOHandler, const std::vector &buffer) { +#ifdef ASSIMP_USE_M3D_READFILECB // pass this IOHandler to the C callback in a thread-local pointer m3dimporter_pIOHandler = pIOHandler; m3d_ = m3d_load(const_cast(buffer.data()), m3dimporter_readfile, free, nullptr); // Clear the C callback m3dimporter_pIOHandler = nullptr; +#else + m3d_ = m3d_load(const_cast(buffer.data()), nullptr, nullptr, nullptr); +#endif } M3DWrapper::~M3DWrapper() { diff --git a/code/M3D/M3DWrapper.h b/code/M3D/M3DWrapper.h index f404d3d4b..4ceac12ad 100644 --- a/code/M3D/M3DWrapper.h +++ b/code/M3D/M3DWrapper.h @@ -52,6 +52,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +// Assimp specific M3D configuration. Comment out these defines to remove functionality +#define ASSIMP_USE_M3D_READFILECB +#define M3D_ASCII + #include "m3d.h" namespace Assimp { diff --git a/code/M3D/m3d.h b/code/M3D/m3d.h index 5ab9f14c0..edf5d9623 100644 --- a/code/M3D/m3d.h +++ b/code/M3D/m3d.h @@ -91,11 +91,7 @@ typedef uint16_t M3D_INDEX; #else #define _inline #define _pack -# ifdef __cplusplus /* only for c++ code */ -# define _unused __pragma(warning(suppress:4100)) -# else -# define _unused (void) -# endif +#define _unused __pragma(warning(suppress:4100)) #endif #ifndef __cplusplus #define _register register @@ -2064,7 +2060,7 @@ static char *_m3d_getfloat(char *s, M3D_FLOAT *ret) return _m3d_findarg(e); } #endif -#if !defined(M3D_NODUP) && (defined(M3D_ASCII) || defined(M3D_EXPORTER)) +#if !defined(M3D_NODUP) && (!defined(M3D_NOIMPORTER) || defined(M3D_ASCII) || defined(M3D_EXPORTER)) /* helper function to create safe strings */ char *_m3d_safestr(char *in, int morelines) { @@ -2139,45 +2135,48 @@ M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char buff = (*readfilecb)(fn2, &len); M3D_FREE(fn2); } - if(!buff) + if(!buff) { buff = (*readfilecb)(fn, &len); + if(!buff) return M3D_UNDEF; + } } - if(!buff) return M3D_UNDEF; /* add to textures array */ i = model->numtexture++; model->texture = (m3dtx_t*)M3D_REALLOC(model->texture, model->numtexture * sizeof(m3dtx_t)); if(!model->texture) { - if(freecb) (*freecb)(buff); + if(buff && freecb) (*freecb)(buff); model->errcode = M3D_ERR_ALLOC; return M3D_UNDEF; } model->texture[i].name = fn; model->texture[i].w = model->texture[i].h = 0; model->texture[i].d = NULL; - if(buff[0] == 0x89 && buff[1] == 'P' && buff[2] == 'N' && buff[3] == 'G') { + if(buff) { + if(buff[0] == 0x89 && buff[1] == 'P' && buff[2] == 'N' && buff[3] == 'G') { #ifdef STBI__PNG_TYPE - s.read_from_callbacks = 0; - s.img_buffer = s.img_buffer_original = (unsigned char *) buff; - s.img_buffer_end = s.img_buffer_original_end = (unsigned char *) buff+len; - /* don't use model->texture[i].w directly, it's a uint16_t */ - w = h = len = 0; - ri.bits_per_channel = 8; - model->texture[i].d = (uint8_t*)stbi__png_load(&s, (int*)&w, (int*)&h, (int*)&len, 0, &ri); - model->texture[i].w = w; - model->texture[i].h = h; - model->texture[i].f = (uint8_t)len; + s.read_from_callbacks = 0; + s.img_buffer = s.img_buffer_original = (unsigned char *) buff; + s.img_buffer_end = s.img_buffer_original_end = (unsigned char *) buff+len; + /* don't use model->texture[i].w directly, it's a uint16_t */ + w = h = len = 0; + ri.bits_per_channel = 8; + model->texture[i].d = (uint8_t*)stbi__png_load(&s, (int*)&w, (int*)&h, (int*)&len, 0, &ri); + model->texture[i].w = w; + model->texture[i].h = h; + model->texture[i].f = (uint8_t)len; #endif - } else { + } else { #ifdef M3D_TX_INTERP - if((model->errcode = M3D_TX_INTERP(fn, buff, len, &model->texture[i])) != M3D_SUCCESS) { - M3D_LOG("Unable to generate texture"); - M3D_LOG(fn); - } + if((model->errcode = M3D_TX_INTERP(fn, buff, len, &model->texture[i])) != M3D_SUCCESS) { + M3D_LOG("Unable to generate texture"); + M3D_LOG(fn); + } #else - M3D_LOG("Unimplemented interpreter"); - M3D_LOG(fn); + M3D_LOG("Unimplemented interpreter"); + M3D_LOG(fn); #endif + } + if(freecb) (*freecb)(buff); } - if(freecb) (*freecb)(buff); if(!model->texture[i].d) model->errcode = M3D_ERR_UNKIMG; return i; @@ -2589,6 +2588,7 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d if(!pe || !*pe) goto asciiend; m->prop[j].value.textureid = _m3d_gettx(model, readfilecb, freecb, pe); if(model->errcode == M3D_ERR_ALLOC) { M3D_FREE(pe); goto memerr; } + /* this error code only returned if readfilecb was specified */ if(m->prop[j].value.textureid == M3D_UNDEF) { M3D_LOG("Texture not found"); M3D_LOG(pe); @@ -3254,6 +3254,7 @@ memerr: M3D_LOG("Out of memory"); M3D_GETSTR(name); m->prop[i].value.textureid = _m3d_gettx(model, readfilecb, freecb, name); if(model->errcode == M3D_ERR_ALLOC) goto memerr; + /* this error code only returned if readfilecb was specified */ if(m->prop[i].value.textureid == M3D_UNDEF) { M3D_LOG("Texture not found"); M3D_LOG(m->name); From 8a800e7aa5df8b18d815b1bab5722e48814e8999 Mon Sep 17 00:00:00 2001 From: bzt Date: Sun, 15 Dec 2019 12:25:34 +0100 Subject: [PATCH 17/36] Removed functionality and minor material-mapping fix --- code/M3D/M3DMaterials.h | 6 +++--- code/M3D/M3DWrapper.h | 4 ++-- code/M3D/m3d.h | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/code/M3D/M3DMaterials.h b/code/M3D/M3DMaterials.h index 3d6fe246d..4aeba59e4 100644 --- a/code/M3D/M3DMaterials.h +++ b/code/M3D/M3DMaterials.h @@ -84,19 +84,19 @@ static const aiMatProp aiProps[] = { /* --- Texture Map Properties --- !!!!! must match m3d_propertytypes !!!!! */ static const aiMatProp aiTxProps[] = { { AI_MATKEY_TEXTURE_DIFFUSE(0) }, /* m3dp_map_Kd */ - { AI_MATKEY_TEXTURE_AMBIENT(0) }, /* m3dp_map_Ka */ + { AI_MATKEY_TEXTURE(aiTextureType_AMBIENT_OCCLUSION,0) },/* m3dp_map_Ka */ { AI_MATKEY_TEXTURE_SPECULAR(0) }, /* m3dp_map_Ks */ { AI_MATKEY_TEXTURE_SHININESS(0) }, /* m3dp_map_Ns */ { AI_MATKEY_TEXTURE_EMISSIVE(0) }, /* m3dp_map_Ke */ { NULL, 0, 0 }, /* m3dp_map_Tf */ { AI_MATKEY_TEXTURE_HEIGHT(0) }, /* m3dp_bump */ { AI_MATKEY_TEXTURE_OPACITY(0) }, /* m3dp_map_d */ - { AI_MATKEY_TEXTURE_REFLECTION(0) }, /* m3dp_refl */ + { AI_MATKEY_TEXTURE_NORMALS(0) }, /* m3dp_map_N */ { AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE_ROUGHNESS,0) },/* m3dp_map_Pr */ { AI_MATKEY_TEXTURE(aiTextureType_METALNESS,0) }, /* m3dp_map_Pm */ { NULL, 0, 0 }, /* m3dp_map_Ps */ - { AI_MATKEY_TEXTURE(aiTextureType_AMBIENT_OCCLUSION,0) },/* m3dp_map_Ni */ + { AI_MATKEY_TEXTURE(aiTextureType_REFLECTION,0) }, /* m3dp_map_Ni */ { NULL, 0, 0 }, /* m3dp_map_Nt */ { NULL, 0, 0 }, { NULL, 0, 0 }, diff --git a/code/M3D/M3DWrapper.h b/code/M3D/M3DWrapper.h index 4ceac12ad..b4f90ff1f 100644 --- a/code/M3D/M3DWrapper.h +++ b/code/M3D/M3DWrapper.h @@ -53,8 +53,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include // Assimp specific M3D configuration. Comment out these defines to remove functionality -#define ASSIMP_USE_M3D_READFILECB -#define M3D_ASCII +//#define ASSIMP_USE_M3D_READFILECB +//#define M3D_ASCII #include "m3d.h" diff --git a/code/M3D/m3d.h b/code/M3D/m3d.h index edf5d9623..d09348497 100644 --- a/code/M3D/m3d.h +++ b/code/M3D/m3d.h @@ -256,7 +256,7 @@ enum { m3dp_map_Tf, m3dp_map_Km, /* bump map */ m3dp_map_D, - m3dp_map_il, /* reflection map */ + m3dp_map_N, /* normal map */ m3dp_map_Pr = 192, /* textured physical map properties */ m3dp_map_Pm, @@ -266,6 +266,7 @@ enum { }; enum { /* aliases */ m3dp_bump = m3dp_map_Km, + m3dp_map_il = m3dp_map_N, m3dp_refl = m3dp_map_Pm }; @@ -560,6 +561,7 @@ static m3dpd_t m3d_propertytypes[] = { /* aliases, note that "map_*" aliases are handled automatically */ M3D_PROPERTYDEF(m3dpf_map, m3dp_map_Km, "bump"), + M3D_PROPERTYDEF(m3dpf_map, m3dp_map_N, "map_N"),/* as normal map has no scalar version, it's counterpart is 'il' */ M3D_PROPERTYDEF(m3dpf_map, m3dp_map_Pm, "refl") }; /* shape command definitions. if more commands start with the same string, the longer must come first */ From eed0bd3ef6fcb79d683fe9b2c0f65afd7d5c12c2 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Lortie Date: Mon, 16 Dec 2019 10:26:46 -0500 Subject: [PATCH 18/36] Added support to load Half-Life 1 MDL files. Added code to use Half-Life 1 MDL loader in MDLLoader.cpp. Added Half-Life 1 MDL loader files to CMakeLists. Added new options in config.h to use with Half-Life 1 MDL loader. --- code/CMakeLists.txt | 10 + code/MDL/HalfLife/HL1FileData.h | 355 ++++++ code/MDL/HalfLife/HL1ImportDefinitions.h | 64 + code/MDL/HalfLife/HL1ImportSettings.h | 85 ++ code/MDL/HalfLife/HL1MDLLoader.cpp | 1331 +++++++++++++++++++++ code/MDL/HalfLife/HL1MDLLoader.h | 235 ++++ code/MDL/HalfLife/HL1MeshTrivert.h | 127 ++ code/MDL/HalfLife/HalfLifeMDLBaseHeader.h | 64 + code/MDL/HalfLife/LogFunctions.h | 95 ++ code/MDL/HalfLife/UniqueNameGenerator.cpp | 180 +++ code/MDL/HalfLife/UniqueNameGenerator.h | 81 ++ code/MDL/MDLLoader.cpp | 44 +- code/MDL/MDLLoader.h | 10 + include/assimp/config.h.in | 67 ++ 14 files changed, 2746 insertions(+), 2 deletions(-) create mode 100644 code/MDL/HalfLife/HL1FileData.h create mode 100644 code/MDL/HalfLife/HL1ImportDefinitions.h create mode 100644 code/MDL/HalfLife/HL1ImportSettings.h create mode 100644 code/MDL/HalfLife/HL1MDLLoader.cpp create mode 100644 code/MDL/HalfLife/HL1MDLLoader.h create mode 100644 code/MDL/HalfLife/HL1MeshTrivert.h create mode 100644 code/MDL/HalfLife/HalfLifeMDLBaseHeader.h create mode 100644 code/MDL/HalfLife/LogFunctions.h create mode 100644 code/MDL/HalfLife/UniqueNameGenerator.cpp create mode 100644 code/MDL/HalfLife/UniqueNameGenerator.h diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 85aa620d9..342433e46 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -454,6 +454,16 @@ ADD_ASSIMP_IMPORTER( MDL MDL/MDLLoader.cpp MDL/MDLLoader.h MDL/MDLMaterialLoader.cpp + MDL/HalfLife/HalfLifeMDLBaseHeader.h + MDL/HalfLife/HL1FileData.h + MDL/HalfLife/HL1MDLLoader.cpp + MDL/HalfLife/HL1MDLLoader.h + MDL/HalfLife/HL1ImportDefinitions.h + MDL/HalfLife/HL1ImportSettings.h + MDL/HalfLife/HL1MeshTrivert.h + MDL/HalfLife/LogFunctions.h + MDL/HalfLife/UniqueNameGenerator.cpp + MDL/HalfLife/UniqueNameGenerator.h ) SET( MaterialSystem_SRCS diff --git a/code/MDL/HalfLife/HL1FileData.h b/code/MDL/HalfLife/HL1FileData.h new file mode 100644 index 000000000..939687a8f --- /dev/null +++ b/code/MDL/HalfLife/HL1FileData.h @@ -0,0 +1,355 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 +#include + +namespace Assimp { +namespace MDL { +namespace HalfLife { + +using vec3_t = float[3]; + +struct Header_HL1 : HalfLifeMDLBaseHeader { + char name[64]; + int32_t length; + + vec3_t eyeposition; // ideal eye position + vec3_t min; // ideal movement hull size + vec3_t max; + + vec3_t bbmin; // clipping bounding box + vec3_t bbmax; + + int32_t unused; // was flags + + int32_t numbones; // bones + int32_t boneindex; + + int32_t numbonecontrollers; // bone controllers + int32_t bonecontrollerindex; + + int32_t numhitboxes; // complex bounding boxes + int32_t hitboxindex; + + int32_t numseq; // animation sequences + int32_t seqindex; + + int32_t numseqgroups; // demand loaded sequences + int32_t seqgroupindex; + + int32_t numtextures; // raw textures + int32_t textureindex; + int32_t texturedataindex; + + int32_t numskinref; // replaceable textures + int32_t numskinfamilies; + int32_t skinindex; + + int32_t numbodyparts; + int32_t bodypartindex; + + int32_t numattachments; // queryable attachable points + int32_t attachmentindex; + + int32_t unused2; // was "soundtable" + int32_t unused3; // was "soundindex" + int32_t unused4; // was "soundgroups" + int32_t unused5; // was "soundgroupindex" + + int32_t numtransitions; // animation node to animation node transition graph + int32_t transitionindex; +} PACK_STRUCT; + +// header for demand loaded sequence group data +struct SequenceHeader_HL1 : HalfLifeMDLBaseHeader { + char name[64]; + int32_t length; +} PACK_STRUCT; + +// bones +struct Bone_HL1 { + char name[32]; // bone name for symbolic links + int32_t parent; // parent bone + int32_t unused; // was "flags" -- ?? + int32_t bonecontroller[6]; // bone controller index, -1 == none + float value[6]; // default DoF values + float scale[6]; // scale for delta DoF values +} PACK_STRUCT; + +// bone controllers +struct BoneController_HL1 { + int32_t bone; // -1 == 0 + int32_t type; // X, Y, Z, XR, YR, ZR, M + float start; + float end; + int32_t unused; // was "rest" - byte index value at rest + int32_t index; // 0-3 user set controller, 4 mouth +} PACK_STRUCT; + +// intersection boxes +struct Hitbox_HL1 { + int32_t bone; + int32_t group; // intersection group + vec3_t bbmin; // bounding box + vec3_t bbmax; +} PACK_STRUCT; + +// +// demand loaded sequence groups +// +struct SequenceGroup_HL1 { + char label[32]; // textual name + char name[64]; // file name + int32_t unused; // was "cache" - index pointer + int32_t unused2; // was "data" - hack for group 0 +} PACK_STRUCT; + +// The type of blending for a sequence. +enum SequenceBlendMode_HL1 { + NoBlend = 1, + TwoWayBlending = 2, + FourWayBlending = 4, +}; + +// sequence descriptions +struct SequenceDesc_HL1 { + char label[32]; // sequence label + + float fps; // frames per second + int32_t flags; // looping/non-looping flags + + int32_t activity; + int32_t actweight; + + int32_t numevents; + int32_t eventindex; + + int32_t numframes; // number of frames per sequence + + int32_t unused; // was "numpivots" - number of foot pivots + int32_t unused2; // was "pivotindex" + + int32_t motiontype; + int32_t motionbone; + vec3_t linearmovement; + int32_t unused3; // was "automoveposindex" + int32_t unused4; // was "automoveangleindex" + + vec3_t bbmin; // per sequence bounding box + vec3_t bbmax; + + int32_t numblends; + int32_t animindex; // mstudioanim_t pointer relative to start of sequence group data + // [blend][bone][X, Y, Z, XR, YR, ZR] + + int32_t blendtype[2]; // X, Y, Z, XR, YR, ZR + float blendstart[2]; // starting value + float blendend[2]; // ending value + int32_t unused5; // was "blendparent" + + int32_t seqgroup; // sequence group for demand loading + + int32_t entrynode; // transition node at entry + int32_t exitnode; // transition node at exit + int32_t nodeflags; // transition rules + + int32_t unused6; // was "nextseq" - auto advancing sequences +} PACK_STRUCT; + +// events +struct AnimEvent_HL1 { + int32_t frame; + int32_t event; + int32_t unused; // was "type" + char options[64]; +} PACK_STRUCT; + +// attachment +struct Attachment_HL1 { + char unused[32]; // was "name" + int32_t unused2; // was "type" + int32_t bone; + vec3_t org; // attachment point + vec3_t unused3[3]; // was "vectors" +} PACK_STRUCT; + +struct AnimValueOffset_HL1 { + unsigned short offset[6]; +} PACK_STRUCT; + +// animation frames +union AnimValue_HL1 { + struct { + uint8_t valid; + uint8_t total; + } num PACK_STRUCT; + short value; +} PACK_STRUCT; + +// body part index +struct Bodypart_HL1 { + char name[64]; + int32_t nummodels; + int32_t base; + int32_t modelindex; // index into models array +} PACK_STRUCT; + +// skin info +struct Texture_HL1 { + char name[64]; + int32_t flags; + int32_t width; + int32_t height; + int32_t index; +} PACK_STRUCT; + +// studio models +struct Model_HL1 { + char name[64]; + + int32_t unused; // was "type" + + float unused2; // was "boundingradius" + + int32_t nummesh; + int32_t meshindex; + + int32_t numverts; // number of unique vertices + int32_t vertinfoindex; // vertex bone info + int32_t vertindex; // vertex vec3_t + int32_t numnorms; // number of unique surface normals + int32_t norminfoindex; // normal bone info + int32_t normindex; // normal vec3_t + + int32_t unused3; // was "numgroups" - deformation groups + int32_t unused4; // was "groupindex" +} PACK_STRUCT; + +// meshes +struct Mesh_HL1 { + int32_t numtris; + int32_t triindex; + int32_t skinref; + int32_t numnorms; // per mesh normals + int32_t unused; // was "normindex" - normal vec3_t +} PACK_STRUCT; + +struct Trivert { + short vertindex; // index into vertex array + short normindex; // index into normal array + short s, t; // s,t position on skin +} PACK_STRUCT; + +#include + +#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/code/MDL/HalfLife/HL1ImportDefinitions.h b/code/MDL/HalfLife/HL1ImportDefinitions.h new file mode 100644 index 000000000..f70f40699 --- /dev/null +++ b/code/MDL/HalfLife/HL1ImportDefinitions.h @@ -0,0 +1,64 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 "" +#define AI_MDL_HL1_NODE_BODYPARTS "" +#define AI_MDL_HL1_NODE_BONES "" +#define AI_MDL_HL1_NODE_BONE_CONTROLLERS "" +#define AI_MDL_HL1_NODE_SEQUENCE_INFOS "" +#define AI_MDL_HL1_NODE_SEQUENCE_GROUPS "" +#define AI_MDL_HL1_NODE_SEQUENCE_TRANSITION_GRAPH "" +#define AI_MDL_HL1_NODE_ATTACHMENTS "" +#define AI_MDL_HL1_NODE_HITBOXES "" +#define AI_MDL_HL1_NODE_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/code/MDL/HalfLife/HL1ImportSettings.h b/code/MDL/HalfLife/HL1ImportSettings.h new file mode 100644 index 000000000..229303e2c --- /dev/null +++ b/code/MDL/HalfLife/HL1ImportSettings.h @@ -0,0 +1,85 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 + +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/code/MDL/HalfLife/HL1MDLLoader.cpp b/code/MDL/HalfLife/HL1MDLLoader.cpp new file mode 100644 index 000000000..a95f65060 --- /dev/null +++ b/code/MDL/HalfLife/HL1MDLLoader.cpp @@ -0,0 +1,1331 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 { + +// ------------------------------------------------------------------------------------------------ +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), + texture_buffer_(nullptr), + anim_buffers_(nullptr), + anim_headers_(nullptr), + num_sequence_groups_(0), + rootnode_children_(), + unique_name_generator_(), + 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; + } +} + +// ------------------------------------------------------------------------------------------------ +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(); + + // Append children to root node. + if (rootnode_children_.size()) { + scene_->mRootNode->addChildren( + static_cast(rootnode_children_.size()), + rootnode_children_.data()); + } + + release_resources(); + + } catch (const std::exception &e) { + release_resources(); + throw e; + } +} + +// ------------------------------------------------------------------------------------------------ +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(header->numtextures, "textures"); + + if (header->numskinfamilies > AI_MDL_HL1_MAX_SKIN_FAMILIES) + log_warning_limit_exceeded(header->numskinfamilies, "skin families"); + + } else { + // Every single Half-Life model is assumed to have at least one bodypart. + if (!header->numbodyparts) + throw DeadlyImportError(MDL_HALFLIFE_LOG_HEADER "Model has no bodyparts"); + + // Every single Half-Life model is assumed to have at least one bone. + if (!header->numbones) + throw DeadlyImportError(MDL_HALFLIFE_LOG_HEADER "Model has no bones"); + + // Every single Half-Life model is assumed to have at least one sequence group, + // which is the "default" sequence group. + if (!header->numseqgroups) + throw DeadlyImportError(MDL_HALFLIFE_LOG_HEADER "Model has no sequence groups"); + + if (header->numbodyparts > AI_MDL_HL1_MAX_BODYPARTS) + log_warning_limit_exceeded(header->numbodyparts, "bodyparts"); + + if (header->numbones > AI_MDL_HL1_MAX_BONES) + log_warning_limit_exceeded(header->numbones, "bones"); + + if (header->numbonecontrollers > AI_MDL_HL1_MAX_BONE_CONTROLLERS) + log_warning_limit_exceeded(header->numbonecontrollers, "bone controllers"); + + if (header->numseq > AI_MDL_HL1_MAX_SEQUENCES) + log_warning_limit_exceeded(header->numseq, "sequences"); + + if (header->numseqgroups > AI_MDL_HL1_MAX_SEQUENCE_GROUPS) + log_warning_limit_exceeded(header->numseqgroups, "sequence groups"); + + if (header->numattachments > AI_MDL_HL1_MAX_ATTACHMENTS) + log_warning_limit_exceeded(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: 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(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(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: 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] = NULL; + anim_headers_[i] = NULL; + } + + 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(sequence_file_path, anim_buffers_[i]); + + anim_headers_[i] = (SequenceHeader_HL1 *)anim_buffers_[i]; + } +} + +// ------------------------------------------------------------------------------------------------ +/** @brief Read an MDL texture. +* +* @note This method is taken from HL1 source code. +* source: file: studio_utils.c +* function(s): UploadTexture +*/ +void HL1MDLLoader::read_texture(const Texture_HL1 *ptexture, + uint8_t *data, uint8_t *pal, aiTexture *pResult, + aiColor3D &last_palette_color) { + int outwidth, outheight; + int i, j; + int row1[256], row2[256], col1[256], col2[256]; + unsigned char *pix1, *pix2, *pix3, *pix4; + + // convert texture to power of 2 + for (outwidth = 1; outwidth < ptexture->width; outwidth <<= 1) + ; + + if (outwidth > 256) + outwidth = 256; + + for (outheight = 1; outheight < ptexture->height; outheight <<= 1) + ; + + if (outheight > 256) + outheight = 256; + + pResult->mFilename = ptexture->name; + pResult->mWidth = outwidth; + pResult->mHeight = outheight; + strncpy(pResult->achFormatHint, "bgra8888", 8); + + aiTexel *out = pResult->pcData = new aiTexel[outwidth * outheight]; + + for (i = 0; i < outwidth; i++) { + col1[i] = (int)((i + 0.25) * (ptexture->width / (float)outwidth)); + col2[i] = (int)((i + 0.75) * (ptexture->width / (float)outwidth)); + } + + for (i = 0; i < outheight; i++) { + row1[i] = (int)((i + 0.25) * (ptexture->height / (float)outheight)) * ptexture->width; + row2[i] = (int)((i + 0.75) * (ptexture->height / (float)outheight)) * ptexture->width; + } + + // scale down and convert to 32bit RGB + for (i = 0; i < outheight; i++) { + for (j = 0; j < outwidth; j++, out++) { + pix1 = &pal[data[row1[i] + col1[j]] * 3]; + pix2 = &pal[data[row1[i] + col2[j]] * 3]; + pix3 = &pal[data[row2[i] + col1[j]] * 3]; + pix4 = &pal[data[row2[i] + col2[j]] * 3]; + + out->r = (pix1[0] + pix2[0] + pix3[0] + pix4[0]) >> 2; + out->g = (pix1[1] + pix2[1] + pix3[1] + pix4[1]) >> 2; + out->b = (pix1[2] + pix2[2] + pix3[2] + pix4[2]) >> 2; + out->a = 0xFF; + } + } + + // 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() { + const Bone_HL1 *pbone = (const Bone_HL1 *)((uint8_t *)header_ + header_->boneindex); + + std::vector 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(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() { + + 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 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(total_verts, "vertices"); + + if (scene_->mNumMeshes > AI_MDL_HL1_MAX_MESHES) + log_warning_limit_exceeded(scene_->mNumMeshes, "meshes"); + + if (total_models_ > AI_MDL_HL1_MAX_MODELS) + log_warning_limit_exceeded(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 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. + + + | + +-- 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(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 bind_pose_vertices; + + // Model_HL1 normals, in bind pose space. + std::vector bind_pose_normals; + + // Used to contain temporary information for building a mesh. + std::vector triverts; + + std::vector tricmds; + + // Which triverts to use for the mesh. + std::vector mesh_triverts_indices; + + std::vector 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> triverts_similars; + + // triverts per bone. + std::map> 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_or_assign(bone, std::set{ 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(mesh_triverts_indices.size()); + + short new_trivert_index = static_cast(triverts.size()); + + if (triverts_similars.count(trivert.vertindex) == 0) + triverts_similars.insert_or_assign(trivert.vertindex, std::set{ 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(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(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(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(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(tricmds.size() - 2); + mesh_faces.reserve(num_faces); + + if (is_triangle_fan) { + for (int i = 0; i < num_faces; ++i) { + mesh_faces.push_back(HL1MeshFace{ + tricmds[0], + tricmds[i + 1], + tricmds[i + 2] }); + } + } else { + for (int i = 0; i < num_faces; ++i) { + if (i & 1) { + // Preserve winding order. + mesh_faces.push_back(HL1MeshFace{ + tricmds[i + 1], + tricmds[i], + tricmds[i + 2] }); + } else { + mesh_faces.push_back(HL1MeshFace{ + tricmds[i], + tricmds[i + 1], + tricmds[i + 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(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(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].v0; + face->mIndices[1] = mesh_faces[f].v1; + face->mIndices[2] = mesh_faces[f].v2; + } + + // Add mesh bones. + scene_mesh->mNumBones = static_cast(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(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(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(pseqdesc->fps) * pseqdesc->numframes; + scene_animation->mNumChannels = static_cast(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(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() { + + 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(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(header_->numseq); + sequence_infos_node->mChildren = new aiNode *[sequence_infos_node->mNumChildren]; + + std::vector 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(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 *md = blend_controller_node->mMetaData = aiMetadata::Alloc(3); + md->Set(0, "Start", pseqdesc->blendstart[j]); + md->Set(1, "End", pseqdesc->blendend[j]); + md->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( + "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(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 *md = pEvent->mMetaData = aiMetadata::Alloc(3); + md->Set(0, "Frame", pevent->frame); + md->Set(1, "ScriptEvent", pevent->event); + md->Set(2, "Options", aiString(pevent->options)); + } + } + + if (sequence_info_node_children.size()) { + sequence_info_node->addChildren( + static_cast(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(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(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(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(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, float &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 (" + std::to_string(num_blend_animations) + ")"); + return false; + } +} + +} // namespace HalfLife +} // namespace MDL +} // namespace Assimp diff --git a/code/MDL/HalfLife/HL1MDLLoader.h b/code/MDL/HalfLife/HL1MDLLoader.h new file mode 100644 index 000000000..f0b227b55 --- /dev/null +++ b/code/MDL/HalfLife/HL1MDLLoader.h @@ -0,0 +1,235 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 +#include +#include + +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 + 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, float &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 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 unique_sequence_names_; + + /** The list of unique sequence groups names. */ + std::vector 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 temp_bones_; + + /** The number of available bone controllers in the model. */ + int num_blend_controllers_; + + /** Self explanatory. */ + int total_models_; +}; + +// ------------------------------------------------------------------------------------------------ +template +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 file(io_->Open(file_path)); + + if (file.get() == NULL) + 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/code/MDL/HalfLife/HL1MeshTrivert.h b/code/MDL/HalfLife/HL1MeshTrivert.h new file mode 100644 index 000000000..38bd371a0 --- /dev/null +++ b/code/MDL/HalfLife/HL1MeshTrivert.h @@ -0,0 +1,127 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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() { + } + + 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/code/MDL/HalfLife/HalfLifeMDLBaseHeader.h b/code/MDL/HalfLife/HalfLifeMDLBaseHeader.h new file mode 100644 index 000000000..496268512 --- /dev/null +++ b/code/MDL/HalfLife/HalfLifeMDLBaseHeader.h @@ -0,0 +1,64 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 + +namespace Assimp { +namespace MDL { +namespace HalfLife { + +/** Used to interface different Valve MDL formats. */ +struct HalfLifeMDLBaseHeader +{ + char ident[4]; + int32_t version; +}; + +} +} +} + +#endif // AI_HALFLIFEMDLBASEHEADER_INCLUDED diff --git a/code/MDL/HalfLife/LogFunctions.h b/code/MDL/HalfLife/LogFunctions.h new file mode 100644 index 000000000..db82a2604 --- /dev/null +++ b/code/MDL/HalfLife/LogFunctions.h @@ -0,0 +1,95 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 +#include + +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 +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 +static inline void log_warning_limit_exceeded(int current_amount, + const std::string &direct_object) { + log_warning_limit_exceeded("Model", current_amount, direct_object); +} + +} // namespace HalfLife +} // namespace MDL +} // namespace Assimp + +#endif // AI_MDL_HALFLIFE_LOGFUNCTIONS_INCLUDED diff --git a/code/MDL/HalfLife/UniqueNameGenerator.cpp b/code/MDL/HalfLife/UniqueNameGenerator.cpp new file mode 100644 index 000000000..8c46b1078 --- /dev/null +++ b/code/MDL/HalfLife/UniqueNameGenerator.cpp @@ -0,0 +1,180 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 +#include +#include +#include + +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 &names) { + struct DuplicateInfo { + DuplicateInfo() : + indices(), + next_id(0) { + } + + std::list indices; + size_t next_id; + }; + + std::vector empty_names_indices; + std::vector template_name_duplicates; + std::map 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)