Browse Source

Merge branch 'master' into Dependabot-GitHub-Actions

Kim Kulling 3 năm trước cách đây
mục cha
commit
4a5ad50828
51 tập tin đã thay đổi với 999 bổ sung608 xóa
  1. 2 1
      Build.md
  2. 1 1
      CMakeLists.txt
  3. 16 0
      SECURITY.md
  4. 2 0
      code/AssetLib/3MF/3MFXmlTags.h
  5. 20 5
      code/AssetLib/3MF/XmlSerializer.cpp
  6. 5 0
      code/AssetLib/Collada/ColladaHelper.h
  7. 6 3
      code/AssetLib/Collada/ColladaLoader.cpp
  8. 1 0
      code/AssetLib/Collada/ColladaLoader.h
  9. 0 21
      code/AssetLib/FBX/FBXAnimation.cpp
  10. 2 1
      code/AssetLib/FBX/FBXCommon.h
  11. 0 1
      code/AssetLib/FBX/FBXCompileConfig.h
  12. 114 52
      code/AssetLib/FBX/FBXConverter.cpp
  13. 26 8
      code/AssetLib/FBX/FBXConverter.h
  14. 2 5
      code/AssetLib/FBX/FBXDeformer.cpp
  15. 2 2
      code/AssetLib/FBX/FBXDocument.cpp
  16. 10 10
      code/AssetLib/FBX/FBXDocument.h
  17. 3 6
      code/AssetLib/FBX/FBXDocumentUtil.cpp
  18. 5 7
      code/AssetLib/FBX/FBXDocumentUtil.h
  19. 1 1
      code/AssetLib/FBX/FBXExporter.cpp
  20. 6 0
      code/AssetLib/FBX/FBXImportSettings.h
  21. 22 25
      code/AssetLib/FBX/FBXImporter.cpp
  22. 7 6
      code/AssetLib/FBX/FBXImporter.h
  23. 4 20
      code/AssetLib/FBX/FBXMeshGeometry.cpp
  24. 12 9
      code/AssetLib/FBX/FBXMeshGeometry.h
  25. 29 78
      code/AssetLib/FBX/FBXNodeAttribute.cpp
  26. 1 15
      code/AssetLib/FBX/FBXParser.cpp
  27. 2 2
      code/AssetLib/FBX/FBXParser.h
  28. 1 1
      code/AssetLib/Irr/IRRLoader.cpp
  29. 1 2
      code/AssetLib/LWO/LWOLoader.cpp
  30. 3 0
      code/AssetLib/MDL/MDLMaterialLoader.cpp
  31. 21 29
      code/AssetLib/OpenGEX/OpenGEXImporter.cpp
  32. 3 8
      code/AssetLib/OpenGEX/OpenGEXImporter.h
  33. 2 2
      code/AssetLib/glTF2/glTF2Exporter.cpp
  34. 10 3
      code/Common/DefaultIOStream.cpp
  35. 29 26
      code/Common/ScenePreprocessor.cpp
  36. 8 6
      code/Common/SkeletonMeshBuilder.cpp
  37. 5 1
      code/Common/Version.cpp
  38. 1 1
      code/PostProcessing/EmbedTexturesProcess.cpp
  39. 4 0
      code/res/assimp.rc
  40. 4 4
      contrib/unzip/crypt.c
  41. 4 2
      contrib/unzip/ioapi.c
  42. 4 4
      contrib/unzip/unzip.c
  43. 273 191
      include/assimp/XmlParser.h
  44. 9 0
      include/assimp/config.h.in
  45. 120 21
      include/assimp/mesh.h
  46. 15 2
      include/assimp/scene.h
  47. 132 0
      test/models/Collada/box_nested_animation.dae
  48. BIN
      test/models/FBX/animation_with_skeleton.fbx
  49. 38 25
      test/unit/utColladaImportExport.cpp
  50. 7 0
      test/unit/utFBXImporterExporter.cpp
  51. 4 1
      test/unit/utOpenGEXImportExport.cpp

+ 2 - 1
Build.md

@@ -14,7 +14,8 @@ The assimp port in vcpkg is kept up to date by Microsoft team members and commun
 ## Install on Ubuntu
 ## Install on Ubuntu
 You can install the Asset-Importer-Lib via apt:
 You can install the Asset-Importer-Lib via apt:
 ```
 ```
-sudo apt-get install assimp
+sudp apt-get update
+sudo apt-get install libassimp-dev
 ```
 ```
 
 
 ## Install pyassimp
 ## Install pyassimp

+ 1 - 1
CMakeLists.txt

@@ -90,7 +90,7 @@ OPTION( ASSIMP_BUILD_ZLIB
 )
 )
 OPTION( ASSIMP_BUILD_ASSIMP_TOOLS
 OPTION( ASSIMP_BUILD_ASSIMP_TOOLS
   "If the supplementary tools for Assimp are built in addition to the library."
   "If the supplementary tools for Assimp are built in addition to the library."
-  ON
+  OFF
 )
 )
 OPTION ( ASSIMP_BUILD_SAMPLES
 OPTION ( ASSIMP_BUILD_SAMPLES
   "If the official samples are built as well (needs Glut)."
   "If the official samples are built as well (needs Glut)."

+ 16 - 0
SECURITY.md

@@ -0,0 +1,16 @@
+# Security Policy
+
+## Supported Versions
+
+Use this section to tell people about which versions of your project are
+currently being supported with security updates.
+
+| Version | Supported          |
+| ------- | ------------------ |
+| 5.2.4   | :white_check_mark: |
+
+## Reporting a Vulnerability
+
+If you have found any security vulnerability you can contact us via 
[email protected]
+

+ 2 - 0
code/AssetLib/3MF/3MFXmlTags.h

@@ -74,6 +74,8 @@ namespace XmlTag {
     const char* const pid = "pid";
     const char* const pid = "pid";
     const char* const pindex = "pindex";
     const char* const pindex = "pindex";
     const char* const p1 = "p1";
     const char* const p1 = "p1";
+    const char *const p2 = "p2";
+    const char *const p3 = "p3";
     const char* const name = "name";
     const char* const name = "name";
     const char* const type = "type";
     const char* const type = "type";
     const char* const build = "build";
     const char* const build = "build";

+ 20 - 5
code/AssetLib/3MF/XmlSerializer.cpp

@@ -64,7 +64,7 @@ bool validateColorString(const char *color) {
     return true;
     return true;
 }
 }
 
 
-aiFace ReadTriangle(XmlNode &node) {
+aiFace ReadTriangle(XmlNode &node, unsigned int &texId0, unsigned int &texId1, unsigned int &texId2) {
     aiFace face;
     aiFace face;
 
 
     face.mNumIndices = 3;
     face.mNumIndices = 3;
@@ -73,6 +73,10 @@ aiFace ReadTriangle(XmlNode &node) {
     face.mIndices[1] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v2).as_string()));
     face.mIndices[1] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v2).as_string()));
     face.mIndices[2] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v3).as_string()));
     face.mIndices[2] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v3).as_string()));
 
 
+    texId0 = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::p1).as_string()));
+    texId1 = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::p2).as_string()));
+    texId2 = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::p3).as_string()));
+
     return face;
     return face;
 }
 }
 
 
@@ -412,6 +416,8 @@ void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) {
             bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid);
             bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid);
             bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1);
             bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1);
 
 
+            unsigned int texId[3];
+            aiFace face = ReadTriangle(currentNode, texId[0], texId[1], texId[2]);
             if (hasPid && hasP1) {
             if (hasPid && hasP1) {
                 auto it = mResourcesDictionnary.find(pid);
                 auto it = mResourcesDictionnary.find(pid);
                 if (it != mResourcesDictionnary.end()) {
                 if (it != mResourcesDictionnary.end()) {
@@ -420,6 +426,11 @@ void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) {
                         mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1];
                         mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1];
                     } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) {
                     } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) {
                         if (mesh->mTextureCoords[0] == nullptr) {
                         if (mesh->mTextureCoords[0] == nullptr) {
+                            mesh->mNumUVComponents[0] = 2;
+                            for (unsigned int i = 1; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+                                mesh->mNumUVComponents[i] = 0;
+                            }
+
                             Texture2DGroup *group = static_cast<Texture2DGroup *>(it->second);
                             Texture2DGroup *group = static_cast<Texture2DGroup *>(it->second);
                             const std::string name = ai_to_string(group->mTexId);
                             const std::string name = ai_to_string(group->mTexId);
                             for (size_t i = 0; i < mMaterials.size(); ++i) {
                             for (size_t i = 0; i < mMaterials.size(); ++i) {
@@ -427,8 +438,9 @@ void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) {
                                     mesh->mMaterialIndex = static_cast<unsigned int>(i);
                                     mesh->mMaterialIndex = static_cast<unsigned int>(i);
                                 }
                                 }
                             }
                             }
-                            mesh->mTextureCoords[0] = new aiVector3D[group->mTex2dCoords.size()];
-                            for (unsigned int i = 0; i < group->mTex2dCoords.size(); ++i) {
+                            mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
+                            for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+
                                 mesh->mTextureCoords[0][i] = aiVector3D(group->mTex2dCoords[i].x, group->mTex2dCoords[i].y, 0);
                                 mesh->mTextureCoords[0][i] = aiVector3D(group->mTex2dCoords[i].x, group->mTex2dCoords[i].y, 0);
                             }
                             }
                         }
                         }
@@ -436,7 +448,6 @@ void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) {
                 }
                 }
             }
             }
 
 
-            aiFace face = ReadTriangle(currentNode);
             faces.push_back(face);
             faces.push_back(face);
         }
         }
     }
     }
@@ -578,11 +589,15 @@ aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basemater
 }
 }
 
 
 void XmlSerializer::StoreMaterialsInScene(aiScene *scene) {
 void XmlSerializer::StoreMaterialsInScene(aiScene *scene) {
-    if (nullptr == scene || mMaterials.empty()) {
+    if (nullptr == scene) {
         return;
         return;
     }
     }
 
 
     scene->mNumMaterials = static_cast<unsigned int>(mMaterials.size());
     scene->mNumMaterials = static_cast<unsigned int>(mMaterials.size());
+    if (scene->mNumMaterials == 0) {
+        return;
+    }
+
     scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
     scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
     for (size_t i = 0; i < mMaterials.size(); ++i) {
     for (size_t i = 0; i < mMaterials.size(); ++i) {
         scene->mMaterials[i] = mMaterials[i];
         scene->mMaterials[i] = mMaterials[i];

+ 5 - 0
code/AssetLib/Collada/ColladaHelper.h

@@ -621,6 +621,11 @@ struct Animation {
 
 
         for (std::vector<Animation *>::iterator it = pParent->mSubAnims.begin(); it != pParent->mSubAnims.end();) {
         for (std::vector<Animation *>::iterator it = pParent->mSubAnims.begin(); it != pParent->mSubAnims.end();) {
             Animation *anim = *it;
             Animation *anim = *it;
+            // Assign the first animation name to the parent if empty.
+            // This prevents the animation name from being lost when animations are combined
+            if (mName.empty()) {
+              mName = anim->mName;
+            }
             CombineSingleChannelAnimationsRecursively(anim);
             CombineSingleChannelAnimationsRecursively(anim);
 
 
             if (childrenAnimationsHaveDifferentChannels && anim->mChannels.size() == 1 &&
             if (childrenAnimationsHaveDifferentChannels && anim->mChannels.size() == 1 &&

+ 6 - 3
code/AssetLib/Collada/ColladaLoader.cpp

@@ -102,6 +102,7 @@ ColladaLoader::ColladaLoader() :
         mTextures(),
         mTextures(),
         mAnims(),
         mAnims(),
         noSkeletonMesh(false),
         noSkeletonMesh(false),
+        removeEmptyBones(false),
         ignoreUpDirection(false),
         ignoreUpDirection(false),
         useColladaName(false),
         useColladaName(false),
         mNodeNameCounter(0) {
         mNodeNameCounter(0) {
@@ -130,6 +131,7 @@ bool ColladaLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void ColladaLoader::SetupProperties(const Importer *pImp) {
 void ColladaLoader::SetupProperties(const Importer *pImp) {
     noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, 0) != 0;
     noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, 0) != 0;
+    removeEmptyBones = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true) != 0;
     ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION, 0) != 0;
     ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION, 0) != 0;
     useColladaName = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES, 0) != 0;
     useColladaName = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES, 0) != 0;
 }
 }
@@ -798,9 +800,10 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc
         // count the number of bones which influence vertices of the current submesh
         // count the number of bones which influence vertices of the current submesh
         size_t numRemainingBones = 0;
         size_t numRemainingBones = 0;
         for (const auto & dstBone : dstBones) {
         for (const auto & dstBone : dstBones) {
-            if (!dstBone.empty()) {
-                ++numRemainingBones;
+            if (dstBone.empty() && removeEmptyBones) {
+                continue;
             }
             }
+            ++numRemainingBones;
         }
         }
 
 
         // create bone array and copy bone weights one by one
         // create bone array and copy bone weights one by one
@@ -809,7 +812,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc
         size_t boneCount = 0;
         size_t boneCount = 0;
         for (size_t a = 0; a < numBones; ++a) {
         for (size_t a = 0; a < numBones; ++a) {
             // omit bones without weights
             // omit bones without weights
-            if (dstBones[a].empty()) {
+            if (dstBones[a].empty() && removeEmptyBones) {
                 continue;
                 continue;
             }
             }
 
 

+ 1 - 0
code/AssetLib/Collada/ColladaLoader.h

@@ -237,6 +237,7 @@ protected:
     std::vector<aiAnimation *> mAnims;
     std::vector<aiAnimation *> mAnims;
 
 
     bool noSkeletonMesh;
     bool noSkeletonMesh;
+    bool removeEmptyBones;
     bool ignoreUpDirection;
     bool ignoreUpDirection;
     bool useColladaName;
     bool useColladaName;
 
 

+ 0 - 21
code/AssetLib/FBX/FBXAnimation.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 
 Copyright (c) 2006-2022, assimp team
 Copyright (c) 2006-2022, assimp team
 
 
-
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,
@@ -87,11 +86,6 @@ AnimationCurve::AnimationCurve(uint64_t id, const Element &element, const std::s
     }
     }
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-AnimationCurve::~AnimationCurve() {
-    // empty
-}
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element &element, const std::string &name,
 AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element &element, const std::string &name,
         const Document &doc, const char *const *target_prop_whitelist /*= nullptr*/,
         const Document &doc, const char *const *target_prop_whitelist /*= nullptr*/,
@@ -147,11 +141,6 @@ AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element &element, cons
     props = GetPropertyTable(doc, "AnimationCurveNode.FbxAnimCurveNode", element, sc, false);
     props = GetPropertyTable(doc, "AnimationCurveNode.FbxAnimCurveNode", element, sc, false);
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-AnimationCurveNode::~AnimationCurveNode() {
-    // empty
-}
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 const AnimationCurveMap &AnimationCurveNode::Curves() const {
 const AnimationCurveMap &AnimationCurveNode::Curves() const {
     if (curves.empty()) {
     if (curves.empty()) {
@@ -193,11 +182,6 @@ AnimationLayer::AnimationLayer(uint64_t id, const Element &element, const std::s
     props = GetPropertyTable(doc, "AnimationLayer.FbxAnimLayer", element, sc, true);
     props = GetPropertyTable(doc, "AnimationLayer.FbxAnimLayer", element, sc, true);
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-AnimationLayer::~AnimationLayer() {
-    // empty
-}
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 AnimationCurveNodeList AnimationLayer::Nodes(const char *const *target_prop_whitelist /*= nullptr*/,
 AnimationCurveNodeList AnimationLayer::Nodes(const char *const *target_prop_whitelist /*= nullptr*/,
         size_t whitelist_size /*= 0*/) const {
         size_t whitelist_size /*= 0*/) const {
@@ -279,11 +263,6 @@ AnimationStack::AnimationStack(uint64_t id, const Element &element, const std::s
     }
     }
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-AnimationStack::~AnimationStack() {
-    // empty
-}
-
 } // namespace FBX
 } // namespace FBX
 } // namespace Assimp
 } // namespace Assimp
 
 

+ 2 - 1
code/AssetLib/FBX/FBXCommon.h

@@ -50,7 +50,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 namespace Assimp {
 namespace Assimp {
 namespace FBX {
 namespace FBX {
 
 
-const std::string NULL_RECORD = { // 25 null bytes in 64-bit and 13 null bytes in 32-bit
+static constexpr size_t NumNullRecords = 25;
+const char NULL_RECORD[NumNullRecords] = { // 25 null bytes in 64-bit and 13 null bytes in 32-bit
     '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
     '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
     '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
     '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
 }; // who knows why, it looks like two integers 32/64 bit (compressed and uncompressed sizes?) + 1 byte (might be compression type?)
 }; // who knows why, it looks like two integers 32/64 bit (compressed and uncompressed sizes?) + 1 byte (might be compression type?)

+ 0 - 1
code/AssetLib/FBX/FBXCompileConfig.h

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 
 Copyright (c) 2006-2022, assimp team
 Copyright (c) 2006-2022, assimp team
 
 
-
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,

+ 114 - 52
code/AssetLib/FBX/FBXConverter.cpp

@@ -65,12 +65,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <stdlib.h>
 #include <stdlib.h>
 #include <cstdint>
 #include <cstdint>
 #include <iomanip>
 #include <iomanip>
-#include <iostream>
 #include <iterator>
 #include <iterator>
 #include <memory>
 #include <memory>
 #include <sstream>
 #include <sstream>
-#include <tuple>
-#include <vector>
 
 
 namespace Assimp {
 namespace Assimp {
 namespace FBX {
 namespace FBX {
@@ -187,8 +184,7 @@ std::string FBXConverter::MakeUniqueNodeName(const Model *const model, const aiN
 
 
 /// This struct manages nodes which may or may not end up in the node hierarchy.
 /// This struct manages nodes which may or may not end up in the node hierarchy.
 /// When a node becomes a child of another node, that node becomes its owner and mOwnership should be released.
 /// When a node becomes a child of another node, that node becomes its owner and mOwnership should be released.
-struct FBXConverter::PotentialNode
-{
+struct FBXConverter::PotentialNode {
     PotentialNode() : mOwnership(new aiNode), mNode(mOwnership.get()) {}
     PotentialNode() : mOwnership(new aiNode), mNode(mOwnership.get()) {}
     PotentialNode(const std::string& name) : mOwnership(new aiNode(name)), mNode(mOwnership.get()) {}
     PotentialNode(const std::string& name) : mOwnership(new aiNode(name)), mNode(mOwnership.get()) {}
     aiNode* operator->() { return mNode; }
     aiNode* operator->() { return mNode; }
@@ -231,7 +227,7 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
         if (nullptr != model) {
         if (nullptr != model) {
             nodes_chain.clear();
             nodes_chain.clear();
             post_nodes_chain.clear();
             post_nodes_chain.clear();
-
+            aiMatrix4x4 new_abs_transform = parent->mTransformation;
             std::string node_name = FixNodeName(model->Name());
             std::string node_name = FixNodeName(model->Name());
             // even though there is only a single input node, the design of
             // even though there is only a single input node, the design of
             // assimp (or rather: the complicated transformation chain that
             // assimp (or rather: the complicated transformation chain that
@@ -268,7 +264,7 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
             }
             }
 
 
             // attach geometry
             // attach geometry
-            ConvertModel(*model, nodes_chain.back().mNode, root_node);
+            ConvertModel(*model, nodes_chain.back().mNode, root_node, new_abs_transform);
 
 
             // check if there will be any child nodes
             // check if there will be any child nodes
             const std::vector<const Connection *> &child_conns = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model");
             const std::vector<const Connection *> &child_conns = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model");
@@ -447,7 +443,7 @@ void FBXConverter::GetUniqueName(const std::string &name, std::string &uniqueNam
     auto it_pair = mNodeNames.insert({ name, 0 }); // duplicate node name instance count
     auto it_pair = mNodeNames.insert({ name, 0 }); // duplicate node name instance count
     unsigned int &i = it_pair.first->second;
     unsigned int &i = it_pair.first->second;
     while (!it_pair.second) {
     while (!it_pair.second) {
-        i++;
+        ++i;
         std::ostringstream ext;
         std::ostringstream ext;
         ext << name << std::setfill('0') << std::setw(3) << i;
         ext << name << std::setfill('0') << std::setw(3) << i;
         uniqueName = ext.str();
         uniqueName = ext.str();
@@ -646,9 +642,8 @@ void FBXConverter::GetRotationMatrix(Model::RotOrder mode, const aiVector3D &rot
 
 
 bool FBXConverter::NeedsComplexTransformationChain(const Model &model) {
 bool FBXConverter::NeedsComplexTransformationChain(const Model &model) {
     const PropertyTable &props = model.Props();
     const PropertyTable &props = model.Props();
-    bool ok;
 
 
-    const float zero_epsilon = ai_epsilon;
+    const auto zero_epsilon = ai_epsilon;
     const aiVector3D all_ones(1.0f, 1.0f, 1.0f);
     const aiVector3D all_ones(1.0f, 1.0f, 1.0f);
     for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
     for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
         const TransformationComp comp = static_cast<TransformationComp>(i);
         const TransformationComp comp = static_cast<TransformationComp>(i);
@@ -660,6 +655,7 @@ bool FBXConverter::NeedsComplexTransformationChain(const Model &model) {
 
 
         bool scale_compare = (comp == TransformationComp_GeometricScaling || comp == TransformationComp_Scaling);
         bool scale_compare = (comp == TransformationComp_GeometricScaling || comp == TransformationComp_Scaling);
 
 
+        bool ok = true;
         const aiVector3D &v = PropertyGet<aiVector3D>(props, NameTransformationCompProperty(comp), ok);
         const aiVector3D &v = PropertyGet<aiVector3D>(props, NameTransformationCompProperty(comp), ok);
         if (ok && scale_compare) {
         if (ok && scale_compare) {
             if ((v - all_ones).SquareLength() > zero_epsilon) {
             if ((v - all_ones).SquareLength() > zero_epsilon) {
@@ -894,18 +890,17 @@ void FBXConverter::SetupNodeMetadata(const Model &model, aiNode &nd) {
     }
     }
 }
 }
 
 
-void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root_node) {
+void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform) {
     const std::vector<const Geometry *> &geos = model.GetGeometry();
     const std::vector<const Geometry *> &geos = model.GetGeometry();
 
 
     std::vector<unsigned int> meshes;
     std::vector<unsigned int> meshes;
     meshes.reserve(geos.size());
     meshes.reserve(geos.size());
 
 
     for (const Geometry *geo : geos) {
     for (const Geometry *geo : geos) {
-
         const MeshGeometry *const mesh = dynamic_cast<const MeshGeometry *>(geo);
         const MeshGeometry *const mesh = dynamic_cast<const MeshGeometry *>(geo);
         const LineGeometry *const line = dynamic_cast<const LineGeometry *>(geo);
         const LineGeometry *const line = dynamic_cast<const LineGeometry *>(geo);
         if (mesh) {
         if (mesh) {
-            const std::vector<unsigned int> &indices = ConvertMesh(*mesh, model, parent, root_node);
+            const std::vector<unsigned int> &indices = ConvertMesh(*mesh, model, parent, root_node, absolute_transform);
             std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
             std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
         } else if (line) {
         } else if (line) {
             const std::vector<unsigned int> &indices = ConvertLine(*line, root_node);
             const std::vector<unsigned int> &indices = ConvertLine(*line, root_node);
@@ -926,7 +921,7 @@ void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root
 }
 }
 
 
 std::vector<unsigned int>
 std::vector<unsigned int>
-FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node) {
+FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform) {
     std::vector<unsigned int> temp;
     std::vector<unsigned int> temp;
 
 
     MeshMap::const_iterator it = meshes_converted.find(&mesh);
     MeshMap::const_iterator it = meshes_converted.find(&mesh);
@@ -949,13 +944,13 @@ FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *
         const MatIndexArray::value_type base = mindices[0];
         const MatIndexArray::value_type base = mindices[0];
         for (MatIndexArray::value_type index : mindices) {
         for (MatIndexArray::value_type index : mindices) {
             if (index != base) {
             if (index != base) {
-                return ConvertMeshMultiMaterial(mesh, model, parent, root_node);
+                return ConvertMeshMultiMaterial(mesh, model, absolute_transform, parent, root_node);
             }
             }
         }
         }
     }
     }
 
 
     // faster code-path, just copy the data
     // faster code-path, just copy the data
-    temp.push_back(ConvertMeshSingleMaterial(mesh, model, parent, root_node));
+    temp.push_back(ConvertMeshSingleMaterial(mesh, model, absolute_transform, parent, root_node));
     return temp;
     return temp;
 }
 }
 
 
@@ -1023,7 +1018,35 @@ aiMesh *FBXConverter::SetupEmptyMesh(const Geometry &mesh, aiNode *parent) {
     return out_mesh;
     return out_mesh;
 }
 }
 
 
-unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model,
+static aiSkeleton *createAiSkeleton(SkeletonBoneContainer &sbc) {
+    if (sbc.MeshArray.empty() || sbc.SkeletonBoneToMeshLookup.empty()) {
+        return nullptr;
+    }
+
+    aiSkeleton *skeleton = new aiSkeleton;
+    for (auto *mesh : sbc.MeshArray) {
+        auto it = sbc.SkeletonBoneToMeshLookup.find(mesh);
+        if (it == sbc.SkeletonBoneToMeshLookup.end()) {
+            continue;
+        }
+        SkeletonBoneArray *ba = it->second;
+        if (ba == nullptr) {
+            continue;
+        }
+
+        skeleton->mNumBones = static_cast<unsigned int>(ba->size());
+        skeleton->mBones = new aiSkeletonBone*[skeleton->mNumBones];
+        size_t index = 0;
+        for (auto bone : (* ba)) {
+            skeleton->mBones[index] = bone;
+            ++index;
+        }
+    }
+
+    return skeleton;
+}
+
+unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model, const aiMatrix4x4 &absolute_transform,
         aiNode *parent, aiNode *) {
         aiNode *parent, aiNode *) {
     const MatIndexArray &mindices = mesh.GetMaterialIndices();
     const MatIndexArray &mindices = mesh.GetMaterialIndices();
     aiMesh *const out_mesh = SetupEmptyMesh(mesh, parent);
     aiMesh *const out_mesh = SetupEmptyMesh(mesh, parent);
@@ -1142,8 +1165,15 @@ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, c
         ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]);
         ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]);
     }
     }
 
 
-    if (doc.Settings().readWeights && mesh.DeformerSkin() != nullptr) {
-        ConvertWeights(out_mesh, mesh, parent, NO_MATERIAL_SEPARATION, nullptr);
+    if (doc.Settings().readWeights && mesh.DeformerSkin() != nullptr && !doc.Settings().useSkeleton) {
+        ConvertWeights(out_mesh, mesh, absolute_transform, parent, NO_MATERIAL_SEPARATION, nullptr);
+    } else if (doc.Settings().readWeights && mesh.DeformerSkin() != nullptr && doc.Settings().useSkeleton) {
+        SkeletonBoneContainer sbc;
+        ConvertWeightsToSkeleton(out_mesh, mesh, absolute_transform, parent, NO_MATERIAL_SEPARATION, nullptr, sbc);
+        aiSkeleton *skeleton = createAiSkeleton(sbc);
+        if (skeleton != nullptr) {
+            mSkeletons.emplace_back(skeleton);
+        }
     }
     }
 
 
     std::vector<aiAnimMesh *> animMeshes;
     std::vector<aiAnimMesh *> animMeshes;
@@ -1190,7 +1220,7 @@ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, c
 }
 }
 
 
 std::vector<unsigned int>
 std::vector<unsigned int>
-FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent,
+FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, const aiMatrix4x4 &absolute_transform, aiNode *parent,
         aiNode *root_node) {
         aiNode *root_node) {
     const MatIndexArray &mindices = mesh.GetMaterialIndices();
     const MatIndexArray &mindices = mesh.GetMaterialIndices();
     ai_assert(mindices.size());
     ai_assert(mindices.size());
@@ -1201,7 +1231,7 @@ FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &mo
     for (MatIndexArray::value_type index : mindices) {
     for (MatIndexArray::value_type index : mindices) {
         if (had.find(index) == had.end()) {
         if (had.find(index) == had.end()) {
 
 
-            indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, parent, root_node));
+            indices.push_back(ConvertMeshMultiMaterial(mesh, model, absolute_transform, index, parent, root_node));
             had.insert(index);
             had.insert(index);
         }
         }
     }
     }
@@ -1209,9 +1239,8 @@ FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &mo
     return indices;
     return indices;
 }
 }
 
 
-unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model,
-        MatIndexArray::value_type index,
-        aiNode *parent, aiNode *) {
+unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, const aiMatrix4x4 &absolute_transform,
+        MatIndexArray::value_type index, aiNode *parent, aiNode *) {
     aiMesh *const out_mesh = SetupEmptyMesh(mesh, parent);
     aiMesh *const out_mesh = SetupEmptyMesh(mesh, parent);
 
 
     const MatIndexArray &mindices = mesh.GetMaterialIndices();
     const MatIndexArray &mindices = mesh.GetMaterialIndices();
@@ -1374,7 +1403,7 @@ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, co
     ConvertMaterialForMesh(out_mesh, model, mesh, index);
     ConvertMaterialForMesh(out_mesh, model, mesh, index);
 
 
     if (process_weights) {
     if (process_weights) {
-        ConvertWeights(out_mesh, mesh, parent, index, &reverseMapping);
+        ConvertWeights(out_mesh, mesh, absolute_transform, parent, index, &reverseMapping);
     }
     }
 
 
     std::vector<aiAnimMesh *> animMeshes;
     std::vector<aiAnimMesh *> animMeshes;
@@ -1424,19 +1453,47 @@ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, co
     return static_cast<unsigned int>(mMeshes.size() - 1);
     return static_cast<unsigned int>(mMeshes.size() - 1);
 }
 }
 
 
-void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo,
+static void copyBoneToSkeletonBone(aiMesh *mesh, aiBone *bone, aiSkeletonBone *skeletonBone ) {
+    skeletonBone->mNumnWeights = bone->mNumWeights;
+    skeletonBone->mWeights = bone->mWeights;
+    skeletonBone->mOffsetMatrix = bone->mOffsetMatrix;
+    skeletonBone->mMeshId = mesh;
+    skeletonBone->mNode = bone->mNode;
+    skeletonBone->mParent = -1;
+}
+
+void FBXConverter::ConvertWeightsToSkeleton(aiMesh *out, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform, aiNode *parent, unsigned int materialIndex,
+        std::vector<unsigned int> *outputVertStartIndices, SkeletonBoneContainer &skeletonContainer) {
+
+    if (skeletonContainer.SkeletonBoneToMeshLookup.find(out) != skeletonContainer.SkeletonBoneToMeshLookup.end()) {
+        return;
+    }
+
+    ConvertWeights(out, geo, absolute_transform, parent, materialIndex, outputVertStartIndices);
+    skeletonContainer.MeshArray.emplace_back(out);
+    SkeletonBoneArray *ba = new SkeletonBoneArray;
+    for (size_t i = 0; i < out->mNumBones; ++i) {
+        aiBone *bone = out->mBones[i];
+        if (bone == nullptr) {
+            continue;
+        }
+        aiSkeletonBone *skeletonBone = new aiSkeletonBone;
+        copyBoneToSkeletonBone(out, bone, skeletonBone);
+        ba->emplace_back(skeletonBone);
+    }
+    skeletonContainer.SkeletonBoneToMeshLookup[out] = ba;
+}
+
+void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform,
         aiNode *parent, unsigned int materialIndex,
         aiNode *parent, unsigned int materialIndex,
         std::vector<unsigned int> *outputVertStartIndices) {
         std::vector<unsigned int> *outputVertStartIndices) {
     ai_assert(geo.DeformerSkin());
     ai_assert(geo.DeformerSkin());
 
 
-    std::vector<size_t> out_indices;
-    std::vector<size_t> index_out_indices;
-    std::vector<size_t> count_out_indices;
+    std::vector<size_t> out_indices, index_out_indices, count_out_indices;
 
 
     const Skin &sk = *geo.DeformerSkin();
     const Skin &sk = *geo.DeformerSkin();
 
 
-    std::vector<aiBone *> bones;
-
+    std::vector<aiBone*> bones;
     const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION;
     const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION;
     ai_assert(no_mat_check || outputVertStartIndices);
     ai_assert(no_mat_check || outputVertStartIndices);
 
 
@@ -1496,7 +1553,7 @@ void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo,
             // XXX this could be heavily simplified by collecting the bone
             // XXX this could be heavily simplified by collecting the bone
             // data in a single step.
             // data in a single step.
             ConvertCluster(bones, cluster, out_indices, index_out_indices,
             ConvertCluster(bones, cluster, out_indices, index_out_indices,
-                    count_out_indices, parent);
+                    count_out_indices, absolute_transform, parent);
         }
         }
 
 
         bone_map.clear();
         bone_map.clear();
@@ -1509,25 +1566,20 @@ void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo,
         out->mBones = nullptr;
         out->mBones = nullptr;
         out->mNumBones = 0;
         out->mNumBones = 0;
         return;
         return;
-    } else {
-        out->mBones = new aiBone *[bones.size()]();
-        out->mNumBones = static_cast<unsigned int>(bones.size());
+    } 
 
 
-        std::swap_ranges(bones.begin(), bones.end(), out->mBones);
-    }
+    out->mBones = new aiBone *[bones.size()]();
+    out->mNumBones = static_cast<unsigned int>(bones.size());
+    std::swap_ranges(bones.begin(), bones.end(), out->mBones);
 }
 }
 
 
-const aiNode *GetNodeByName(aiNode *current_node) {
-    aiNode *iter = current_node;
-    //printf("Child count: %d", iter->mNumChildren);
-    return iter;
-}
-
-void FBXConverter::ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl,
+void FBXConverter::ConvertCluster(std::vector<aiBone*> &local_mesh_bones, const Cluster *cluster,
         std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
         std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
-        std::vector<size_t> &count_out_indices, aiNode *) {
-    ai_assert(cl); // make sure cluster valid
-    std::string deformer_name = cl->TargetNode()->Name();
+        std::vector<size_t> &count_out_indices, const aiMatrix4x4 & /* absolute_transform*/,
+        aiNode *) {
+    ai_assert(cluster != nullptr); // make sure cluster valid
+
+    std::string deformer_name = cluster->TargetNode()->Name();
     aiString bone_name = aiString(FixNodeName(deformer_name));
     aiString bone_name = aiString(FixNodeName(deformer_name));
 
 
     aiBone *bone = nullptr;
     aiBone *bone = nullptr;
@@ -1540,10 +1592,10 @@ void FBXConverter::ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const
         bone = new aiBone();
         bone = new aiBone();
         bone->mName = bone_name;
         bone->mName = bone_name;
 
 
-        bone->mOffsetMatrix = cl->Transform();
+        bone->mOffsetMatrix = cluster->Transform();
         // store local transform link for post processing
         // store local transform link for post processing
         /*
         /*
-        bone->mOffsetMatrix = cl->TransformLink();
+        bone->mOffsetMatrix = cluster->TransformLink();
         bone->mOffsetMatrix.Inverse();
         bone->mOffsetMatrix.Inverse();
 
 
         aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
         aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
@@ -1560,7 +1612,7 @@ void FBXConverter::ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const
         cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
         cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
 
 
         const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
         const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
-        const WeightArray &weights = cl->GetWeights();
+        const WeightArray &weights = cluster->GetWeights();
 
 
         const size_t c = index_out_indices.size();
         const size_t c = index_out_indices.size();
         for (size_t i = 0; i < c; ++i) {
         for (size_t i = 0; i < c; ++i) {
@@ -2605,7 +2657,7 @@ void FBXConverter::ConvertAnimationStack(const AnimationStack &st) {
                 meshMorphAnim->mNumKeys = numKeys;
                 meshMorphAnim->mNumKeys = numKeys;
                 meshMorphAnim->mKeys = new aiMeshMorphKey[numKeys];
                 meshMorphAnim->mKeys = new aiMeshMorphKey[numKeys];
                 unsigned int j = 0;
                 unsigned int j = 0;
-                for (auto animIt : *animData) {
+                for (auto &animIt : *animData) {
                     morphKeyData *keyData = animIt.second;
                     morphKeyData *keyData = animIt.second;
                     unsigned int numValuesAndWeights = static_cast<unsigned int>(keyData->values.size());
                     unsigned int numValuesAndWeights = static_cast<unsigned int>(keyData->values.size());
                     meshMorphAnim->mKeys[j].mNumValuesAndWeights = numValuesAndWeights;
                     meshMorphAnim->mKeys[j].mNumValuesAndWeights = numValuesAndWeights;
@@ -3180,7 +3232,7 @@ aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim(const std::string& name,
 
 
     bool ok = false;
     bool ok = false;
     
     
-    const float zero_epsilon = ai_epsilon;
+    const auto zero_epsilon = ai_epsilon;
 
 
     const aiVector3D& preRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok);
     const aiVector3D& preRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok);
     if (ok && preRotation.SquareLength() > zero_epsilon) {
     if (ok && preRotation.SquareLength() > zero_epsilon) {
@@ -3325,6 +3377,10 @@ FBXConverter::KeyFrameListList FBXConverter::GetRotationKeyframeList(const std::
                             Keys->push_back(tnew);
                             Keys->push_back(tnew);
                             Values->push_back(vnew);
                             Values->push_back(vnew);
                         }
                         }
+                        else {
+                            // Something broke
+                            break;
+                        }
                         tp = tnew;
                         tp = tnew;
                         vp = vnew;
                         vp = vnew;
                     }
                     }
@@ -3625,6 +3681,12 @@ void FBXConverter::TransferDataToScene() {
 
 
         std::swap_ranges(textures.begin(), textures.end(), mSceneOut->mTextures);
         std::swap_ranges(textures.begin(), textures.end(), mSceneOut->mTextures);
     }
     }
+
+    if (!mSkeletons.empty()) {
+        mSceneOut->mSkeletons = new aiSkeleton *[mSkeletons.size()];
+        mSceneOut->mNumSkeletons = static_cast<unsigned int>(mSkeletons.size());
+        std::swap_ranges(mSkeletons.begin(), mSkeletons.end(), mSceneOut->mSkeletons);
+    }
 }
 }
 
 
 void FBXConverter::ConvertOrphanedEmbeddedTextures() {
 void FBXConverter::ConvertOrphanedEmbeddedTextures() {

+ 26 - 8
code/AssetLib/FBX/FBXConverter.h

@@ -75,7 +75,18 @@ typedef std::map<int64_t, morphKeyData*> morphAnimData;
 namespace Assimp {
 namespace Assimp {
 namespace FBX {
 namespace FBX {
 
 
+class MeshGeometry;
+
+using SkeletonBoneArray = std::vector<aiSkeletonBone *>;
+using SkeletonBoneToMesh = std::map<aiMesh*, SkeletonBoneArray*>;
+
+struct SkeletonBoneContainer {
+    std::vector<aiMesh *> MeshArray;
+    SkeletonBoneToMesh SkeletonBoneToMeshLookup;
+};
+
 class Document;
 class Document;
+
 /**
 /**
  *  Convert a FBX #Document to #aiScene
  *  Convert a FBX #Document to #aiScene
  *  @param out Empty scene to be populated
  *  @param out Empty scene to be populated
@@ -180,12 +191,12 @@ private:
     void SetupNodeMetadata(const Model& model, aiNode& nd);
     void SetupNodeMetadata(const Model& model, aiNode& nd);
 
 
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
-    void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node);
+    void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform);
 
 
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
     // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
     // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
     std::vector<unsigned int>
     std::vector<unsigned int>
-    ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node);
+    ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform);
 
 
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
     std::vector<unsigned int> ConvertLine(const LineGeometry& line, aiNode *root_node);
     std::vector<unsigned int> ConvertLine(const LineGeometry& line, aiNode *root_node);
@@ -194,15 +205,15 @@ private:
     aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode *parent);
     aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode *parent);
 
 
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
-    unsigned int ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model,
+    unsigned int ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model, const aiMatrix4x4 &absolute_transform,
                                            aiNode *parent, aiNode *root_node);
                                            aiNode *parent, aiNode *root_node);
 
 
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
     std::vector<unsigned int>
     std::vector<unsigned int>
-    ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node);
+    ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, const aiMatrix4x4 &absolute_transform, aiNode *parent, aiNode *root_node);
 
 
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
-    unsigned int ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, MatIndexArray::value_type index,
+    unsigned int ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, const aiMatrix4x4 &absolute_transform, MatIndexArray::value_type index,
                                           aiNode *parent, aiNode *root_node);
                                           aiNode *parent, aiNode *root_node);
 
 
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
@@ -216,14 +227,19 @@ private:
     *  - outputVertStartIndices is only used when a material index is specified, it gives for
     *  - outputVertStartIndices is only used when a material index is specified, it gives for
     *    each output vertex the DOM index it maps to.
     *    each output vertex the DOM index it maps to.
     */
     */
-    void ConvertWeights(aiMesh *out, const MeshGeometry &geo, aiNode *parent = nullptr,
+    void ConvertWeights(aiMesh *out, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform, aiNode *parent = nullptr,
             unsigned int materialIndex = NO_MATERIAL_SEPARATION,
             unsigned int materialIndex = NO_MATERIAL_SEPARATION,
             std::vector<unsigned int> *outputVertStartIndices = nullptr);
             std::vector<unsigned int> *outputVertStartIndices = nullptr);
 
 
+    // ------------------------------------------------------------------------------------------------
+    void ConvertWeightsToSkeleton(aiMesh *out, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform,
+            aiNode *parent, unsigned int materialIndex, std::vector<unsigned int> *outputVertStartIndices,
+            SkeletonBoneContainer &skeletonContainer);
+
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
     void ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl,
     void ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl,
                         std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
                         std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
-                        std::vector<size_t> &count_out_indices, aiNode *parent );
+            std::vector<size_t> &count_out_indices, const aiMatrix4x4 &absolute_transform, aiNode *parent);
 
 
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
     void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,
     void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,
@@ -296,7 +312,8 @@ private:
     void ConvertAnimationStack(const AnimationStack& st);
     void ConvertAnimationStack(const AnimationStack& st);
 
 
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
-    void ProcessMorphAnimDatas(std::map<std::string, morphAnimData*>* morphAnimDatas, const BlendShapeChannel* bsc, const AnimationCurveNode* node);
+    void ProcessMorphAnimDatas(std::map<std::string, morphAnimData*>* morphAnimDatas,
+        const BlendShapeChannel* bsc, const AnimationCurveNode* node);
 
 
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
     void GenerateNodeAnimations(std::vector<aiNodeAnim*>& node_anims,
     void GenerateNodeAnimations(std::vector<aiNodeAnim*>& node_anims,
@@ -445,6 +462,7 @@ private:
 
 
     double anim_fps;
     double anim_fps;
 
 
+    std::vector<aiSkeleton *> mSkeletons;
     aiScene* const mSceneOut;
     aiScene* const mSceneOut;
     const FBX::Document& doc;
     const FBX::Document& doc;
     bool mRemoveEmptyBones;
     bool mRemoveEmptyBones;

+ 2 - 5
code/AssetLib/FBX/FBXDeformer.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 
 Copyright (c) 2006-2022, assimp team
 Copyright (c) 2006-2022, assimp team
 
 
-
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,
@@ -58,16 +57,14 @@ namespace FBX {
 using namespace Util;
 using namespace Util;
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-Deformer::Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name)
-    : Object(id,element,name)
-{
+Deformer::Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name) :
+        Object(id,element,name) {
     const Scope& sc = GetRequiredScope(element);
     const Scope& sc = GetRequiredScope(element);
 
 
     const std::string& classname = ParseTokenAsString(GetRequiredToken(element,2));
     const std::string& classname = ParseTokenAsString(GetRequiredToken(element,2));
     props = GetPropertyTable(doc,"Deformer.Fbx" + classname,element,sc,true);
     props = GetPropertyTable(doc,"Deformer.Fbx" + classname,element,sc,true);
 }
 }
 
 
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 Deformer::~Deformer()
 Deformer::~Deformer()
 {
 {

+ 2 - 2
code/AssetLib/FBX/FBXDocument.cpp

@@ -67,7 +67,7 @@ namespace FBX {
 using namespace Util;
 using namespace Util;
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc) : 
+LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc) :
         doc(doc), element(element), id(id), flags() {
         doc(doc), element(element), id(id), flags() {
     // empty
     // empty
 }
 }
@@ -544,7 +544,7 @@ std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bo
     ai_assert( count != 0 );
     ai_assert( count != 0 );
     ai_assert( count <= MAX_CLASSNAMES);
     ai_assert( count <= MAX_CLASSNAMES);
 
 
-    size_t lengths[MAX_CLASSNAMES];
+    size_t lengths[MAX_CLASSNAMES] = {};
 
 
     const size_t c = count;
     const size_t c = count;
     for (size_t i = 0; i < c; ++i) {
     for (size_t i = 0; i < c; ++i) {

+ 10 - 10
code/AssetLib/FBX/FBXDocument.h

@@ -164,7 +164,7 @@ class NodeAttribute : public Object {
 public:
 public:
     NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name);
 
 
-    virtual ~NodeAttribute();
+    virtual ~NodeAttribute() = default;
 
 
     const PropertyTable& Props() const {
     const PropertyTable& Props() const {
         ai_assert(props.get());
         ai_assert(props.get());
@@ -180,7 +180,7 @@ class CameraSwitcher : public NodeAttribute {
 public:
 public:
     CameraSwitcher(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     CameraSwitcher(uint64_t id, const Element& element, const Document& doc, const std::string& name);
 
 
-    virtual ~CameraSwitcher();
+    virtual ~CameraSwitcher() = default;
 
 
     int CameraID() const {
     int CameraID() const {
         return cameraId;
         return cameraId;
@@ -225,7 +225,7 @@ class Camera : public NodeAttribute {
 public:
 public:
     Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name);
 
 
-    virtual  ~Camera();
+    virtual  ~Camera() = default;
 
 
     fbx_simple_property(Position, aiVector3D, aiVector3D(0,0,0))
     fbx_simple_property(Position, aiVector3D, aiVector3D(0,0,0))
     fbx_simple_property(UpVector, aiVector3D, aiVector3D(0,1,0))
     fbx_simple_property(UpVector, aiVector3D, aiVector3D(0,1,0))
@@ -250,21 +250,21 @@ public:
 class Null : public NodeAttribute {
 class Null : public NodeAttribute {
 public:
 public:
     Null(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     Null(uint64_t id, const Element& element, const Document& doc, const std::string& name);
-    virtual ~Null();
+    virtual ~Null() = default;
 };
 };
 
 
 /** DOM base class for FBX limb node markers attached to a node */
 /** DOM base class for FBX limb node markers attached to a node */
 class LimbNode : public NodeAttribute {
 class LimbNode : public NodeAttribute {
 public:
 public:
     LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name);
-    virtual ~LimbNode();
+    virtual ~LimbNode() = default;
 };
 };
 
 
 /** DOM base class for FBX lights attached to a node */
 /** DOM base class for FBX lights attached to a node */
 class Light : public NodeAttribute {
 class Light : public NodeAttribute {
 public:
 public:
     Light(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     Light(uint64_t id, const Element& element, const Document& doc, const std::string& name);
-    virtual ~Light();
+    virtual ~Light() = default;
 
 
     enum Type {
     enum Type {
         Type_Point,
         Type_Point,
@@ -690,7 +690,7 @@ using KeyValueList = std::vector<float>;
 class AnimationCurve : public Object {
 class AnimationCurve : public Object {
 public:
 public:
     AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& doc);
     AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& doc);
-    virtual ~AnimationCurve();
+    virtual ~AnimationCurve() = default;
 
 
     /** get list of keyframe positions (time).
     /** get list of keyframe positions (time).
      *  Invariant: |GetKeys()| > 0 */
      *  Invariant: |GetKeys()| > 0 */
@@ -731,7 +731,7 @@ public:
     AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, const Document& doc,
     AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, const Document& doc,
             const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0);
             const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0);
 
 
-    virtual ~AnimationCurveNode();
+    virtual ~AnimationCurveNode() = default;
 
 
     const PropertyTable& Props() const {
     const PropertyTable& Props() const {
         ai_assert(props.get());
         ai_assert(props.get());
@@ -776,7 +776,7 @@ using AnimationCurveNodeList = std::vector<const AnimationCurveNode*>;
 class AnimationLayer : public Object {
 class AnimationLayer : public Object {
 public:
 public:
     AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc);
     AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc);
-    virtual ~AnimationLayer();
+    virtual ~AnimationLayer() = default;
 
 
     const PropertyTable& Props() const {
     const PropertyTable& Props() const {
         ai_assert(props.get());
         ai_assert(props.get());
@@ -799,7 +799,7 @@ using AnimationLayerList = std::vector<const AnimationLayer*>;
 class AnimationStack : public Object {
 class AnimationStack : public Object {
 public:
 public:
     AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc);
     AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc);
-    virtual ~AnimationStack();
+    virtual ~AnimationStack() = default;
 
 
     fbx_simple_property(LocalStart, int64_t, 0L)
     fbx_simple_property(LocalStart, int64_t, 0L)
     fbx_simple_property(LocalStop, int64_t, 0L)
     fbx_simple_property(LocalStop, int64_t, 0L)

+ 3 - 6
code/AssetLib/FBX/FBXDocumentUtil.cpp

@@ -59,14 +59,12 @@ namespace Util {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // signal DOM construction error, this is always unrecoverable. Throws DeadlyImportError.
 // signal DOM construction error, this is always unrecoverable. Throws DeadlyImportError.
-void DOMError(const std::string& message, const Token& token)
-{
+void DOMError(const std::string& message, const Token& token) {
     throw DeadlyImportError("FBX-DOM", Util::GetTokenText(&token), message);
     throw DeadlyImportError("FBX-DOM", Util::GetTokenText(&token), message);
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-void DOMError(const std::string& message, const Element* element /*= nullptr*/)
-{
+void DOMError(const std::string& message, const Element* element /*= nullptr*/) {
     if(element) {
     if(element) {
         DOMError(message,element->KeyToken());
         DOMError(message,element->KeyToken());
     }
     }
@@ -76,8 +74,7 @@ void DOMError(const std::string& message, const Element* element /*= nullptr*/)
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // print warning, do return
 // print warning, do return
-void DOMWarning(const std::string& message, const Token& token)
-{
+void DOMWarning(const std::string& message, const Token& token) {
     if(DefaultLogger::get()) {
     if(DefaultLogger::get()) {
         ASSIMP_LOG_WARN("FBX-DOM", Util::GetTokenText(&token), message);
         ASSIMP_LOG_WARN("FBX-DOM", Util::GetTokenText(&token), message);
     }
     }

+ 5 - 7
code/AssetLib/FBX/FBXDocumentUtil.h

@@ -74,13 +74,11 @@ std::shared_ptr<const PropertyTable> GetPropertyTable(const Document& doc,
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 template <typename T>
 template <typename T>
-inline
-const T* ProcessSimpleConnection(const Connection& con,
-    bool is_object_property_conn,
-    const char* name,
-    const Element& element,
-    const char** propNameOut = nullptr)
-{
+inline const T* ProcessSimpleConnection(const Connection& con,
+        bool is_object_property_conn,
+        const char* name,
+        const Element& element,
+        const char** propNameOut = nullptr) {
     if (is_object_property_conn && !con.PropertyName().length()) {
     if (is_object_property_conn && !con.PropertyName().length()) {
         DOMWarning("expected incoming " + std::string(name) +
         DOMWarning("expected incoming " + std::string(name) +
             " link to be an object-object connection, ignoring",
             " link to be an object-object connection, ignoring",

+ 1 - 1
code/AssetLib/FBX/FBXExporter.cpp

@@ -255,7 +255,7 @@ void FBXExporter::WriteBinaryHeader()
 
 
 void FBXExporter::WriteBinaryFooter()
 void FBXExporter::WriteBinaryFooter()
 {
 {
-    outfile->Write(NULL_RECORD.c_str(), NULL_RECORD.size(), 1);
+    outfile->Write(NULL_RECORD, NumNullRecords, 1);
 
 
     outfile->Write(GENERIC_FOOTID.c_str(), GENERIC_FOOTID.size(), 1);
     outfile->Write(GENERIC_FOOTID.c_str(), GENERIC_FOOTID.size(), 1);
 
 

+ 6 - 0
code/AssetLib/FBX/FBXImportSettings.h

@@ -60,6 +60,7 @@ struct ImportSettings {
             readLights(true),
             readLights(true),
             readAnimations(true),
             readAnimations(true),
             readWeights(true),
             readWeights(true),
+            useSkeleton(false),
             preservePivots(true),
             preservePivots(true),
             optimizeEmptyAnimationCurves(true),
             optimizeEmptyAnimationCurves(true),
             useLegacyEmbeddedTextureNaming(false),
             useLegacyEmbeddedTextureNaming(false),
@@ -112,6 +113,11 @@ struct ImportSettings {
      *  Default value is true. */
      *  Default value is true. */
     bool readWeights;
     bool readWeights;
 
 
+    /** will convert all animation data into a skeleton (experimental)
+     *  Default value is false.
+     */
+    bool useSkeleton;
+
     /** preserve transformation pivots and offsets. Since these can
     /** preserve transformation pivots and offsets. Since these can
      *  not directly be represented in assimp, additional dummy
      *  not directly be represented in assimp, additional dummy
      *  nodes will be generated. Note that settings this to false
      *  nodes will be generated. Note that settings this to false

+ 22 - 25
code/AssetLib/FBX/FBXImporter.cpp

@@ -90,12 +90,9 @@ static const aiImporterDesc desc = {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by #Importer
 // Constructor to be privately used by #Importer
-FBXImporter::FBXImporter() {
-}
-
-// ------------------------------------------------------------------------------------------------
-// Destructor, private as well
-FBXImporter::~FBXImporter() {
+FBXImporter::FBXImporter() :
+        mSettings() {
+    // empty
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -115,20 +112,21 @@ const aiImporterDesc *FBXImporter::GetInfo() const {
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Setup configuration properties for the loader
 // Setup configuration properties for the loader
 void FBXImporter::SetupProperties(const Importer *pImp) {
 void FBXImporter::SetupProperties(const Importer *pImp) {
-	settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true);
-	settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false);
-	settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
-	settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true);
-	settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
-	settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
-	settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true);
-	settings.readWeights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_WEIGHTS, true);
-	settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false);
-	settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
-	settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
-	settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
-	settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
-	settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false);
+    mSettings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true);
+    mSettings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false);
+    mSettings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
+    mSettings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true);
+    mSettings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
+    mSettings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
+    mSettings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true);
+    mSettings.readWeights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_WEIGHTS, true);
+    mSettings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false);
+    mSettings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
+    mSettings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
+    mSettings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
+    mSettings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
+    mSettings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false);
+    mSettings.useSkeleton = pImp->GetPropertyBool(AI_CONFIG_FBX_USE_SKELETON_BONE_CONTAINER, false);
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -155,7 +153,7 @@ void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 	contents[contents.size() - 1] = 0;
 	contents[contents.size() - 1] = 0;
 	const char *const begin = &*contents.begin();
 	const char *const begin = &*contents.begin();
 
 
-	// broadphase tokenizing pass in which we identify the core
+	// broad-phase tokenized pass in which we identify the core
 	// syntax elements of FBX (brackets, commas, key:value mappings)
 	// syntax elements of FBX (brackets, commas, key:value mappings)
 	TokenList tokens;
 	TokenList tokens;
 	try {
 	try {
@@ -173,15 +171,14 @@ void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 		Parser parser(tokens, is_binary);
 		Parser parser(tokens, is_binary);
 
 
 		// take the raw parse-tree and convert it to a FBX DOM
 		// take the raw parse-tree and convert it to a FBX DOM
-		Document doc(parser, settings);
+		Document doc(parser, mSettings);
 
 
 		// convert the FBX DOM to aiScene
 		// convert the FBX DOM to aiScene
-		ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones);
+		ConvertToAssimpScene(pScene, doc, mSettings.removeEmptyBones);
 
 
 		// size relative to cm
 		// size relative to cm
 		float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor();
 		float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor();
-        if (size_relative_to_cm == 0.0)
-        {
+        if (size_relative_to_cm == 0.0) {
 			// BaseImporter later asserts that fileScale is non-zero.
 			// BaseImporter later asserts that fileScale is non-zero.
 			ThrowException("The UnitScaleFactor must be non-zero");
 			ThrowException("The UnitScaleFactor must be non-zero");
         }
         }

+ 7 - 6
code/AssetLib/FBX/FBXImporter.h

@@ -69,13 +69,14 @@ typedef class basic_formatter<char, std::char_traits<char>, std::allocator<char>
 // -------------------------------------------------------------------------------------------
 // -------------------------------------------------------------------------------------------
 class FBXImporter : public BaseImporter, public LogFunctions<FBXImporter> {
 class FBXImporter : public BaseImporter, public LogFunctions<FBXImporter> {
 public:
 public:
+    /// @brief The class constructor.
     FBXImporter();
     FBXImporter();
-    ~FBXImporter() override;
 
 
-    // --------------------
-    bool CanRead(const std::string &pFile,
-            IOSystem *pIOHandler,
-            bool checkSig) const override;
+    ///	@brief The class destructor, default implementation.
+    ~FBXImporter() override = default;
+
+    /// @brief Will check the file for readability.
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
 
 
 protected:
 protected:
     // --------------------
     // --------------------
@@ -90,7 +91,7 @@ protected:
             IOSystem *pIOHandler) override;
             IOSystem *pIOHandler) override;
 
 
 private:
 private:
-    FBX::ImportSettings settings;
+    FBX::ImportSettings mSettings;
 }; // !class FBXImporter
 }; // !class FBXImporter
 
 
 } // end of namespace Assimp
 } // end of namespace Assimp

+ 4 - 20
code/AssetLib/FBX/FBXMeshGeometry.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 
 Copyright (c) 2006-2022, assimp team
 Copyright (c) 2006-2022, assimp team
 
 
-
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,
@@ -54,18 +53,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "FBXImportSettings.h"
 #include "FBXImportSettings.h"
 #include "FBXDocumentUtil.h"
 #include "FBXDocumentUtil.h"
 
 
-
 namespace Assimp {
 namespace Assimp {
 namespace FBX {
 namespace FBX {
 
 
 using namespace Util;
 using namespace Util;
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
-    : Object(id, element, name)
-    , skin()
-{
-    const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer");
+Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) :
+        Object(id, element, name), skin() {
+    const std::vector<const Connection*> &conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer");
     for(const Connection* con : conns) {
     for(const Connection* con : conns) {
         const Skin* const sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element);
         const Skin* const sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element);
         if(sk) {
         if(sk) {
@@ -78,12 +74,6 @@ Geometry::Geometry(uint64_t id, const Element& element, const std::string& name,
     }
     }
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-Geometry::~Geometry()
-{
-    // empty
-}
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 const std::vector<const BlendShape*>& Geometry::GetBlendShapes() const {
 const std::vector<const BlendShape*>& Geometry::GetBlendShapes() const {
     return blendShapes;
     return blendShapes;
@@ -183,18 +173,12 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
         if(doc.Settings().readAllLayers || index == 0) {
         if(doc.Settings().readAllLayers || index == 0) {
             const Scope& layer = GetRequiredScope(*(*it).second);
             const Scope& layer = GetRequiredScope(*(*it).second);
             ReadLayer(layer);
             ReadLayer(layer);
-        }
-        else {
+        } else {
             FBXImporter::LogWarn("ignoring additional geometry layers");
             FBXImporter::LogWarn("ignoring additional geometry layers");
         }
         }
     }
     }
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-MeshGeometry::~MeshGeometry() {
-    // empty
-}
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 const std::vector<aiVector3D>& MeshGeometry::GetVertices() const {
 const std::vector<aiVector3D>& MeshGeometry::GetVertices() const {
     return m_vertices;
     return m_vertices;

+ 12 - 9
code/AssetLib/FBX/FBXMeshGeometry.h

@@ -55,22 +55,25 @@ namespace FBX {
 /**
 /**
  *  DOM base class for all kinds of FBX geometry
  *  DOM base class for all kinds of FBX geometry
  */
  */
-class Geometry : public Object
-{
+class Geometry : public Object {
 public:
 public:
+    /// @brief The class constructor with all parameters.
+    /// @param id       The id.
+    /// @param element  
+    /// @param name 
+    /// @param doc 
     Geometry( uint64_t id, const Element& element, const std::string& name, const Document& doc );
     Geometry( uint64_t id, const Element& element, const std::string& name, const Document& doc );
-    virtual ~Geometry();
+    virtual ~Geometry() = default;
 
 
-    /** Get the Skin attached to this geometry or nullptr */
+    /// Get the Skin attached to this geometry or nullptr
     const Skin* DeformerSkin() const;
     const Skin* DeformerSkin() const;
 
 
-    /** Get the BlendShape attached to this geometry or nullptr */
+    /// Get the BlendShape attached to this geometry or nullptr
     const std::vector<const BlendShape*>& GetBlendShapes() const;
     const std::vector<const BlendShape*>& GetBlendShapes() const;
 
 
 private:
 private:
     const Skin* skin;
     const Skin* skin;
     std::vector<const BlendShape*> blendShapes;
     std::vector<const BlendShape*> blendShapes;
-
 };
 };
 
 
 typedef std::vector<int> MatIndexArray;
 typedef std::vector<int> MatIndexArray;
@@ -79,14 +82,13 @@ typedef std::vector<int> MatIndexArray;
 /**
 /**
  *  DOM class for FBX geometry of type "Mesh"
  *  DOM class for FBX geometry of type "Mesh"
  */
  */
-class MeshGeometry : public Geometry
-{
+class MeshGeometry : public Geometry {
 public:
 public:
     /** The class constructor */
     /** The class constructor */
     MeshGeometry( uint64_t id, const Element& element, const std::string& name, const Document& doc );
     MeshGeometry( uint64_t id, const Element& element, const std::string& name, const Document& doc );
 
 
     /** The class destructor */
     /** The class destructor */
-    virtual ~MeshGeometry();
+    virtual ~MeshGeometry() = default;
 
 
     /** Get a list of all vertex points, non-unique*/
     /** Get a list of all vertex points, non-unique*/
     const std::vector<aiVector3D>& GetVertices() const;
     const std::vector<aiVector3D>& GetVertices() const;
@@ -130,6 +132,7 @@ public:
     /** Determine the face to which a particular output vertex index belongs.
     /** Determine the face to which a particular output vertex index belongs.
     *  This mapping is always unique. */
     *  This mapping is always unique. */
     unsigned int FaceForVertexIndex( unsigned int in_index ) const;
     unsigned int FaceForVertexIndex( unsigned int in_index ) const;
+
 private:
 private:
     void ReadLayer( const Scope& layer );
     void ReadLayer( const Scope& layer );
     void ReadLayerElement( const Scope& layerElement );
     void ReadLayerElement( const Scope& layerElement );

+ 29 - 78
code/AssetLib/FBX/FBXNodeAttribute.cpp

@@ -57,114 +57,65 @@ namespace FBX {
 using namespace Util;
 using namespace Util;
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-NodeAttribute::NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name)
-: Object(id,element,name)
-, props()
-{
-    const Scope& sc = GetRequiredScope(element);
+NodeAttribute::NodeAttribute(uint64_t id, const Element &element, const Document &doc, const std::string &name) :
+        Object(id, element, name), props() {
+    const Scope &sc = GetRequiredScope(element);
 
 
-    const std::string& classname = ParseTokenAsString(GetRequiredToken(element,2));
+    const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
 
 
     // hack on the deriving type but Null/LimbNode attributes are the only case in which
     // hack on the deriving type but Null/LimbNode attributes are the only case in which
     // the property table is by design absent and no warning should be generated
     // the property table is by design absent and no warning should be generated
     // for it.
     // for it.
     const bool is_null_or_limb = !strcmp(classname.c_str(), "Null") || !strcmp(classname.c_str(), "LimbNode");
     const bool is_null_or_limb = !strcmp(classname.c_str(), "Null") || !strcmp(classname.c_str(), "LimbNode");
-    props = GetPropertyTable(doc,"NodeAttribute.Fbx" + classname,element,sc, is_null_or_limb);
+    props = GetPropertyTable(doc, "NodeAttribute.Fbx" + classname, element, sc, is_null_or_limb);
 }
 }
 
 
-
-// ------------------------------------------------------------------------------------------------
-NodeAttribute::~NodeAttribute()
-{
-    // empty
-}
-
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-CameraSwitcher::CameraSwitcher(uint64_t id, const Element& element, const Document& doc, const std::string& name)
-    : NodeAttribute(id,element,doc,name)
-{
-    const Scope& sc = GetRequiredScope(element);
-    const Element* const CameraId = sc["CameraId"];
-    const Element* const CameraName = sc["CameraName"];
-    const Element* const CameraIndexName = sc["CameraIndexName"];
-
-    if(CameraId) {
-        cameraId = ParseTokenAsInt(GetRequiredToken(*CameraId,0));
+CameraSwitcher::CameraSwitcher(uint64_t id, const Element &element, const Document &doc, const std::string &name) :
+        NodeAttribute(id, element, doc, name) {
+    const Scope &sc = GetRequiredScope(element);
+    const Element *const CameraId = sc["CameraId"];
+    const Element *const CameraName = sc["CameraName"];
+    const Element *const CameraIndexName = sc["CameraIndexName"];
+
+    if (CameraId) {
+        cameraId = ParseTokenAsInt(GetRequiredToken(*CameraId, 0));
     }
     }
 
 
-    if(CameraName) {
-        cameraName = GetRequiredToken(*CameraName,0).StringContents();
+    if (CameraName) {
+        cameraName = GetRequiredToken(*CameraName, 0).StringContents();
     }
     }
 
 
-    if(CameraIndexName && CameraIndexName->Tokens().size()) {
-        cameraIndexName = GetRequiredToken(*CameraIndexName,0).StringContents();
+    if (CameraIndexName && CameraIndexName->Tokens().size()) {
+        cameraIndexName = GetRequiredToken(*CameraIndexName, 0).StringContents();
     }
     }
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-CameraSwitcher::~CameraSwitcher()
-{
+Camera::Camera(uint64_t id, const Element &element, const Document &doc, const std::string &name) :
+        NodeAttribute(id, element, doc, name) {
     // empty
     // empty
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-Camera::Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name)
-: NodeAttribute(id,element,doc,name)
-{
+Light::Light(uint64_t id, const Element &element, const Document &doc, const std::string &name) :
+        NodeAttribute(id, element, doc, name) {
     // empty
     // empty
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-Camera::~Camera()
-{
+Null::Null(uint64_t id, const Element &element, const Document &doc, const std::string &name) :
+        NodeAttribute(id, element, doc, name) {
     // empty
     // empty
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-Light::Light(uint64_t id, const Element& element, const Document& doc, const std::string& name)
-: NodeAttribute(id,element,doc,name)
-{
+LimbNode::LimbNode(uint64_t id, const Element &element, const Document &doc, const std::string &name) :
+        NodeAttribute(id, element, doc, name) {
     // empty
     // empty
 }
 }
 
 
+} // namespace FBX
+} // namespace Assimp
 
 
-// ------------------------------------------------------------------------------------------------
-Light::~Light()
-{
-}
-
-
-// ------------------------------------------------------------------------------------------------
-Null::Null(uint64_t id, const Element& element, const Document& doc, const std::string& name)
-: NodeAttribute(id,element,doc,name)
-{
-
-}
-
-
-// ------------------------------------------------------------------------------------------------
-Null::~Null()
-{
-
-}
-
-
-// ------------------------------------------------------------------------------------------------
-LimbNode::LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name)
-: NodeAttribute(id,element,doc,name)
-{
-
-}
-
-
-// ------------------------------------------------------------------------------------------------
-LimbNode::~LimbNode()
-{
-
-}
-
-}
-}
-
-#endif
+#endif // ASSIMP_BUILD_NO_FBX_IMPORTER

+ 1 - 15
code/AssetLib/FBX/FBXParser.cpp

@@ -162,12 +162,6 @@ Element::Element(const Token& key_token, Parser& parser) : key_token(key_token)
     while(n->Type() != TokenType_KEY && n->Type() != TokenType_CLOSE_BRACKET);
     while(n->Type() != TokenType_KEY && n->Type() != TokenType_CLOSE_BRACKET);
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-Element::~Element()
-{
-     // no need to delete tokens, they are owned by the parser
-}
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 Scope::Scope(Parser& parser,bool topLevel)
 Scope::Scope(Parser& parser,bool topLevel)
 {
 {
@@ -226,12 +220,6 @@ Parser::Parser (const TokenList& tokens, bool is_binary)
     root.reset(new Scope(*this,true));
     root.reset(new Scope(*this,true));
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-Parser::~Parser()
-{
-    // empty
-}
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 TokenPtr Parser::AdvanceToNextToken()
 TokenPtr Parser::AdvanceToNextToken()
 {
 {
@@ -961,8 +949,7 @@ void ParseVectorDataArray(std::vector<float>& out, const Element& el)
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // read an array of uints
 // read an array of uints
-void ParseVectorDataArray(std::vector<unsigned int>& out, const Element& el)
-{
+void ParseVectorDataArray(std::vector<unsigned int>& out, const Element& el) {
     out.resize( 0 );
     out.resize( 0 );
     const TokenList& tok = el.Tokens();
     const TokenList& tok = el.Tokens();
     if(tok.empty()) {
     if(tok.empty()) {
@@ -1186,7 +1173,6 @@ aiMatrix4x4 ReadMatrix(const Element& element)
     return result;
     return result;
 }
 }
 
 
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // wrapper around ParseTokenAsString() with ParseError handling
 // wrapper around ParseTokenAsString() with ParseError handling
 std::string ParseTokenAsString(const Token& t)
 std::string ParseTokenAsString(const Token& t)

+ 2 - 2
code/AssetLib/FBX/FBXParser.h

@@ -87,7 +87,7 @@ class Element
 {
 {
 public:
 public:
     Element(const Token& key_token, Parser& parser);
     Element(const Token& key_token, Parser& parser);
-    ~Element();
+    ~Element() = default;
 
 
     const Scope* Compound() const {
     const Scope* Compound() const {
         return compound.get();
         return compound.get();
@@ -160,7 +160,7 @@ public:
     /** Parse given a token list. Does not take ownership of the tokens -
     /** Parse given a token list. Does not take ownership of the tokens -
      *  the objects must persist during the entire parser lifetime */
      *  the objects must persist during the entire parser lifetime */
     Parser (const TokenList& tokens,bool is_binary);
     Parser (const TokenList& tokens,bool is_binary);
-    ~Parser();
+    ~Parser() = default;
 
 
     const Scope& GetRootScope() const {
     const Scope& GetRootScope() const {
         return *root.get();
         return *root.get();

+ 1 - 1
code/AssetLib/Irr/IRRLoader.cpp

@@ -874,7 +874,7 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
 
 	// Batch loader used to load external models
 	// Batch loader used to load external models
 	BatchLoader batch(pIOHandler);
 	BatchLoader batch(pIOHandler);
-	//  batch.SetBasePath(pFile);
+	//batch.SetBasePath(pFile);
 
 
 	cameras.reserve(5);
 	cameras.reserve(5);
 	lights.reserve(5);
 	lights.reserve(5);

+ 1 - 2
code/AssetLib/LWO/LWOLoader.cpp

@@ -287,7 +287,7 @@ void LWOImporter::InternReadFile(const std::string &pFile,
             if (UINT_MAX == iDefaultSurface) {
             if (UINT_MAX == iDefaultSurface) {
                 pSorted.erase(pSorted.end() - 1);
                 pSorted.erase(pSorted.end() - 1);
             }
             }
-            for (unsigned int p = 0, j = 0; j < mSurfaces->size(); ++j) {
+            for (unsigned int j = 0; j < mSurfaces->size(); ++j) {
                 SortedRep &sorted = pSorted[j];
                 SortedRep &sorted = pSorted[j];
                 if (sorted.empty())
                 if (sorted.empty())
                     continue;
                     continue;
@@ -425,7 +425,6 @@ void LWOImporter::InternReadFile(const std::string &pFile,
                 } else {
                 } else {
                     ASSIMP_LOG_VERBOSE_DEBUG("LWO2: No need to compute normals, they're already there");
                     ASSIMP_LOG_VERBOSE_DEBUG("LWO2: No need to compute normals, they're already there");
                 }
                 }
-                ++p;
             }
             }
         }
         }
 
 

+ 3 - 0
code/AssetLib/MDL/MDLMaterialLoader.cpp

@@ -449,6 +449,9 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
         unsigned int iWidth,
         unsigned int iWidth,
         unsigned int iHeight) {
         unsigned int iHeight) {
     std::unique_ptr<aiTexture> pcNew;
     std::unique_ptr<aiTexture> pcNew;
+    if (szCurrent == nullptr) {
+        return;
+    }
 
 
     // get the type of the skin
     // get the type of the skin
     unsigned int iMasked = (unsigned int)(iType & 0xF);
     unsigned int iMasked = (unsigned int)(iType & 0xF);

+ 21 - 29
code/AssetLib/OpenGEX/OpenGEXImporter.cpp

@@ -151,45 +151,46 @@ namespace Grammar {
     }
     }
 
 
     static TokenType matchTokenType(const char *tokenType) {
     static TokenType matchTokenType(const char *tokenType) {
-        if (MetricType == tokenType) {
+        const size_t len = std::strlen(tokenType);
+        if (0 == strncmp(MetricType, tokenType, len)) {
             return MetricToken;
             return MetricToken;
-        } else if (NameType == tokenType) {
+        } else if (0 == strncmp(NameType, tokenType, len)) {
             return NameToken;
             return NameToken;
-        } else if (ObjectRefType == tokenType) {
+        } else if (0 == strncmp(ObjectRefType, tokenType, len)) {
             return ObjectRefToken;
             return ObjectRefToken;
-        } else if (MaterialRefType == tokenType) {
+        } else if (0 == strncmp(MaterialRefType, tokenType, len)) {
             return MaterialRefToken;
             return MaterialRefToken;
-        } else if (MetricKeyType == tokenType) {
+        } else if (0 == strncmp(MetricKeyType, tokenType, len)) {
             return MetricKeyToken;
             return MetricKeyToken;
-        } else if (GeometryNodeType == tokenType) {
+        } else if (0 == strncmp(GeometryNodeType, tokenType, len)) {
             return GeometryNodeToken;
             return GeometryNodeToken;
-        } else if (CameraNodeType == tokenType) {
+        } else if (0 == strncmp(CameraNodeType, tokenType, len)) {
             return CameraNodeToken;
             return CameraNodeToken;
-        } else if (LightNodeType == tokenType) {
+        } else if (0 == strncmp(LightNodeType, tokenType, len)) {
             return LightNodeToken;
             return LightNodeToken;
-        } else if (GeometryObjectType == tokenType) {
+        } else if (0 == strncmp(GeometryObjectType, tokenType, len)) {
             return GeometryObjectToken;
             return GeometryObjectToken;
-        } else if (CameraObjectType == tokenType) {
+        } else if (0 == strncmp(CameraObjectType, tokenType, len)) {
             return CameraObjectToken;
             return CameraObjectToken;
-        } else if (LightObjectType == tokenType) {
+        } else if (0 == strncmp(LightObjectType, tokenType, len)) {
             return LightObjectToken;
             return LightObjectToken;
-        } else if (TransformType == tokenType) {
+        } else if (0 == strncmp(TransformType, tokenType, len)) {
             return TransformToken;
             return TransformToken;
-        } else if (MeshType == tokenType) {
+        } else if (0 == strncmp(MeshType, tokenType, len)) {
             return MeshToken;
             return MeshToken;
-        } else if (VertexArrayType == tokenType) {
+        } else if (0 == strncmp(VertexArrayType, tokenType, len)) {
             return VertexArrayToken;
             return VertexArrayToken;
-        } else if (IndexArrayType == tokenType) {
+        } else if (0 == strncmp(IndexArrayType, tokenType, len)) {
             return IndexArrayToken;
             return IndexArrayToken;
-        } else if (MaterialType == tokenType) {
+        } else if (0 == strncmp(MaterialType, tokenType, len)) {
             return MaterialToken;
             return MaterialToken;
-        } else if (ColorType == tokenType) {
+        } else if (0 == strncmp(ColorType, tokenType, len)) {
             return ColorToken;
             return ColorToken;
-        } else if (ParamType == tokenType) {
+        } else if (0 == strncmp(ParamType, tokenType, len)) {
             return ParamToken;
             return ParamToken;
-        } else if (TextureType == tokenType) {
+        } else if (0 == strncmp(TextureType, tokenType, len)) {
             return TextureToken;
             return TextureToken;
-        } else if (AttenType == tokenType) {
+        } else if (0 == strncmp(AttenType, tokenType, len)) {
             return AttenToken;
             return AttenToken;
         }
         }
 
 
@@ -256,11 +257,6 @@ OpenGEXImporter::RefInfo::RefInfo(aiNode *node, Type type, std::vector<std::stri
     // empty
     // empty
 }
 }
 
 
-//------------------------------------------------------------------------------------------------
-OpenGEXImporter::RefInfo::~RefInfo() {
-    // empty
-}
-
 //------------------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------------------
 OpenGEXImporter::OpenGEXImporter() :
 OpenGEXImporter::OpenGEXImporter() :
         m_root(nullptr),
         m_root(nullptr),
@@ -285,10 +281,6 @@ OpenGEXImporter::OpenGEXImporter() :
     // empty
     // empty
 }
 }
 
 
-//------------------------------------------------------------------------------------------------
-OpenGEXImporter::~OpenGEXImporter() {
-}
-
 //------------------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------------------
 bool OpenGEXImporter::CanRead(const std::string &file, IOSystem *pIOHandler, bool /*checkSig*/) const {
 bool OpenGEXImporter::CanRead(const std::string &file, IOSystem *pIOHandler, bool /*checkSig*/) const {
     static const char *tokens[] = { "Metric", "GeometryNode", "VertexArray (attrib", "IndexArray" };
     static const char *tokens[] = { "Metric", "GeometryNode", "VertexArray (attrib", "IndexArray" };

+ 3 - 8
code/AssetLib/OpenGEX/OpenGEXImporter.h

@@ -79,12 +79,7 @@ struct MetricInfo {
     float m_floatValue;
     float m_floatValue;
     int m_intValue;
     int m_intValue;
 
 
-    MetricInfo()
-    : m_stringValue( )
-    , m_floatValue( 0.0f )
-    , m_intValue( -1 ) {
-        // empty
-    }
+    MetricInfo(): m_stringValue( ), m_floatValue( 0.0f ), m_intValue( -1 ) {}
 };
 };
 
 
 /** @brief  This class is used to implement the OpenGEX importer
 /** @brief  This class is used to implement the OpenGEX importer
@@ -97,7 +92,7 @@ public:
     OpenGEXImporter();
     OpenGEXImporter();
 
 
     /// The class destructor.
     /// The class destructor.
-    ~OpenGEXImporter() override;
+    ~OpenGEXImporter() override = default;
 
 
     /// BaseImporter override.
     /// BaseImporter override.
     bool CanRead( const std::string &file, IOSystem *pIOHandler, bool checkSig ) const override;
     bool CanRead( const std::string &file, IOSystem *pIOHandler, bool checkSig ) const override;
@@ -170,7 +165,7 @@ private:
         std::vector<std::string> m_Names;
         std::vector<std::string> m_Names;
 
 
         RefInfo( aiNode *node, Type type, std::vector<std::string> &names );
         RefInfo( aiNode *node, Type type, std::vector<std::string> &names );
-        ~RefInfo();
+        ~RefInfo() = default;
 
 
         RefInfo( const RefInfo & ) = delete;
         RefInfo( const RefInfo & ) = delete;
         RefInfo &operator = ( const RefInfo & ) = delete;
         RefInfo &operator = ( const RefInfo & ) = delete;

+ 2 - 2
code/AssetLib/glTF2/glTF2Exporter.cpp

@@ -683,7 +683,7 @@ bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &she
     }
     }
 
 
     // Default Sheen color factor {0,0,0} disables Sheen, so do not export
     // Default Sheen color factor {0,0,0} disables Sheen, so do not export
-    if (sheen.sheenColorFactor == defaultSheenFactor) {
+    if (sheen.sheenColorFactor[0] == defaultSheenFactor[0] && sheen.sheenColorFactor[1] == defaultSheenFactor[1] && sheen.sheenColorFactor[2] == defaultSheenFactor[2]) {
         return false;
         return false;
     }
     }
 
 
@@ -908,7 +908,7 @@ Ref<Node> FindSkeletonRootJoint(Ref<Skin> &skinRef) {
     do {
     do {
         startNodeRef = parentNodeRef;
         startNodeRef = parentNodeRef;
         parentNodeRef = startNodeRef->parent;
         parentNodeRef = startNodeRef->parent;
-    } while (!parentNodeRef->jointName.empty());
+    } while (parentNodeRef && !parentNodeRef->jointName.empty());
 
 
     return parentNodeRef;
     return parentNodeRef;
 }
 }

+ 10 - 3
code/Common/DefaultIOStream.cpp

@@ -63,7 +63,7 @@ inline int select_fseek(FILE *file, int64_t offset, int origin) {
 
 
 
 
 
 
-#if defined _WIN64 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
+#if defined _WIN32 && (!defined __GNUC__ || !defined __CLANG__ && __MSVCRT_VERSION__ >= 0x0601)
 template <>
 template <>
 inline size_t select_ftell<8>(FILE *file) {
 inline size_t select_ftell<8>(FILE *file) {
     return (size_t)::_ftelli64(file);
     return (size_t)::_ftelli64(file);
@@ -74,7 +74,7 @@ inline int select_fseek<8>(FILE *file, int64_t offset, int origin) {
     return ::_fseeki64(file, offset, origin);
     return ::_fseeki64(file, offset, origin);
 }
 }
 
 
-#endif // #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
+#endif
 
 
 } // namespace
 } // namespace
 
 
@@ -149,13 +149,20 @@ size_t DefaultIOStream::FileSize() const {
         //
         //
         // See here for details:
         // See here for details:
         // https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file
         // https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file
-#if defined _WIN64 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
+#if defined _WIN32 && (!defined __GNUC__ || !defined __CLANG__ && __MSVCRT_VERSION__ >= 0x0601)
         struct __stat64 fileStat;
         struct __stat64 fileStat;
         //using fileno + fstat avoids having to handle the filename
         //using fileno + fstat avoids having to handle the filename
         int err = _fstat64(_fileno(mFile), &fileStat);
         int err = _fstat64(_fileno(mFile), &fileStat);
         if (0 != err)
         if (0 != err)
             return 0;
             return 0;
         mCachedSize = (size_t)(fileStat.st_size);
         mCachedSize = (size_t)(fileStat.st_size);
+#elif defined _WIN32
+        struct _stat32 fileStat;
+        //using fileno + fstat avoids having to handle the filename
+        int err = _fstat32(_fileno(mFile), &fileStat);
+        if (0 != err)
+            return 0;
+        mCachedSize = (size_t)(fileStat.st_size);
 #elif defined __GNUC__ || defined __APPLE__ || defined __MACH__ || defined __FreeBSD__
 #elif defined __GNUC__ || defined __APPLE__ || defined __MACH__ || defined __FreeBSD__
         struct stat fileStat;
         struct stat fileStat;
         int err = stat(mFilename.c_str(), &fileStat);
         int err = stat(mFilename.c_str(), &fileStat);

+ 29 - 26
code/Common/ScenePreprocessor.cpp

@@ -105,36 +105,39 @@ void ScenePreprocessor::ProcessMesh(aiMesh *mesh) {
     for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
     for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
         if (!mesh->mTextureCoords[i]) {
         if (!mesh->mTextureCoords[i]) {
             mesh->mNumUVComponents[i] = 0;
             mesh->mNumUVComponents[i] = 0;
-        } else {
-            if (!mesh->mNumUVComponents[i]) {
-                mesh->mNumUVComponents[i] = 2;
-            }
+            continue;
+        } 
 
 
-            aiVector3D *p = mesh->mTextureCoords[i], *end = p + mesh->mNumVertices;
+        if (!mesh->mNumUVComponents[i]) {
+            mesh->mNumUVComponents[i] = 2;
+        }
 
 
-            // Ensure unused components are zeroed. This will make 1D texture channels work
-            // as if they were 2D channels .. just in case an application doesn't handle
-            // this case
-            if (2 == mesh->mNumUVComponents[i]) {
-                for (; p != end; ++p) {
-                    p->z = 0.f;
-                }
-            } else if (1 == mesh->mNumUVComponents[i]) {
-                for (; p != end; ++p) {
-                    p->z = p->y = 0.f;
-                }
-            } else if (3 == mesh->mNumUVComponents[i]) {
-                // Really 3D coordinates? Check whether the third coordinate is != 0 for at least one element
-                for (; p != end; ++p) {
-                    if (p->z != 0) {
-                        break;
-                    }
-                }
-                if (p == end) {
-                    ASSIMP_LOG_WARN("ScenePreprocessor: UVs are declared to be 3D but they're obviously not. Reverting to 2D.");
-                    mesh->mNumUVComponents[i] = 2;
+        aiVector3D *p = mesh->mTextureCoords[i], *end = p + mesh->mNumVertices;
+
+        // Ensure unused components are zeroed. This will make 1D texture channels work
+        // as if they were 2D channels .. just in case an application doesn't handle
+        // this case
+        if (2 == mesh->mNumUVComponents[i]) {
+            size_t num = 0;
+            for (; p != end; ++p) {
+                p->z = 0.f;
+                num++;
+            }
+        } else if (1 == mesh->mNumUVComponents[i]) {
+            for (; p != end; ++p) {
+                p->z = p->y = 0.f;
+            }
+        } else if (3 == mesh->mNumUVComponents[i]) {
+            // Really 3D coordinates? Check whether the third coordinate is != 0 for at least one element
+            for (; p != end; ++p) {
+                if (p->z != 0) {
+                    break;
                 }
                 }
             }
             }
+            if (p == end) {
+                ASSIMP_LOG_WARN("ScenePreprocessor: UVs are declared to be 3D but they're obviously not. Reverting to 2D.");
+                mesh->mNumUVComponents[i] = 2;
+            }
         }
         }
     }
     }
 
 

+ 8 - 6
code/Common/SkeletonMeshBuilder.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 
 Copyright (c) 2006-2022, assimp team
 Copyright (c) 2006-2022, assimp team
 
 
-
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,
@@ -97,13 +96,14 @@ void SkeletonMeshBuilder::CreateGeometry(const aiNode *pNode) {
             const aiMatrix4x4 &childTransform = pNode->mChildren[a]->mTransformation;
             const aiMatrix4x4 &childTransform = pNode->mChildren[a]->mTransformation;
             aiVector3D childpos(childTransform.a4, childTransform.b4, childTransform.c4);
             aiVector3D childpos(childTransform.a4, childTransform.b4, childTransform.c4);
             ai_real distanceToChild = childpos.Length();
             ai_real distanceToChild = childpos.Length();
-            if (distanceToChild < 0.0001)
+            if (distanceToChild < ai_epsilon) {
                 continue;
                 continue;
+            }
             aiVector3D up = aiVector3D(childpos).Normalize();
             aiVector3D up = aiVector3D(childpos).Normalize();
-
             aiVector3D orth(1.0, 0.0, 0.0);
             aiVector3D orth(1.0, 0.0, 0.0);
-            if (std::fabs(orth * up) > 0.99)
+            if (std::fabs(orth * up) > 0.99) {
                 orth.Set(0.0, 1.0, 0.0);
                 orth.Set(0.0, 1.0, 0.0);
+            }
 
 
             aiVector3D front = (up ^ orth).Normalize();
             aiVector3D front = (up ^ orth).Normalize();
             aiVector3D side = (front ^ up).Normalize();
             aiVector3D side = (front ^ up).Normalize();
@@ -183,8 +183,9 @@ void SkeletonMeshBuilder::CreateGeometry(const aiNode *pNode) {
         // add all the vertices to the bone's influences
         // add all the vertices to the bone's influences
         bone->mNumWeights = numVertices;
         bone->mNumWeights = numVertices;
         bone->mWeights = new aiVertexWeight[numVertices];
         bone->mWeights = new aiVertexWeight[numVertices];
-        for (unsigned int a = 0; a < numVertices; a++)
+        for (unsigned int a = 0; a < numVertices; ++a) {
             bone->mWeights[a] = aiVertexWeight(vertexStartIndex + a, 1.0);
             bone->mWeights[a] = aiVertexWeight(vertexStartIndex + a, 1.0);
+        }
 
 
         // HACK: (thom) transform all vertices to the bone's local space. Should be done before adding
         // HACK: (thom) transform all vertices to the bone's local space. Should be done before adding
         // them to the array, but I'm tired now and I'm annoyed.
         // them to the array, but I'm tired now and I'm annoyed.
@@ -194,8 +195,9 @@ void SkeletonMeshBuilder::CreateGeometry(const aiNode *pNode) {
     }
     }
 
 
     // and finally recurse into the children list
     // and finally recurse into the children list
-    for (unsigned int a = 0; a < pNode->mNumChildren; a++)
+    for (unsigned int a = 0; a < pNode->mNumChildren; ++a) {
         CreateGeometry(pNode->mChildren[a]);
         CreateGeometry(pNode->mChildren[a]);
+    }
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------

+ 5 - 1
code/Common/Version.cpp

@@ -135,6 +135,9 @@ ASSIMP_API aiScene::aiScene() :
         mNumCameras(0),
         mNumCameras(0),
         mCameras(nullptr),
         mCameras(nullptr),
         mMetaData(nullptr),
         mMetaData(nullptr),
+        mName(),
+        mNumSkeletons(0),
+        mSkeletons(nullptr),
         mPrivate(new Assimp::ScenePrivateData()) {
         mPrivate(new Assimp::ScenePrivateData()) {
     // empty
     // empty
 }
 }
@@ -180,7 +183,8 @@ ASSIMP_API aiScene::~aiScene() {
     delete[] mCameras;
     delete[] mCameras;
 
 
     aiMetadata::Dealloc(mMetaData);
     aiMetadata::Dealloc(mMetaData);
-    mMetaData = nullptr;
 
 
+    delete[] mSkeletons;
+    
     delete static_cast<Assimp::ScenePrivateData *>(mPrivate);
     delete static_cast<Assimp::ScenePrivateData *>(mPrivate);
 }
 }

+ 1 - 1
code/PostProcessing/EmbedTexturesProcess.cpp

@@ -128,7 +128,7 @@ bool EmbedTexturesProcess::addTexture(aiScene *pScene, const std::string &path)
 
 
     aiTexel* imageContent = new aiTexel[ 1ul + static_cast<unsigned long>( imageSize ) / sizeof(aiTexel)];
     aiTexel* imageContent = new aiTexel[ 1ul + static_cast<unsigned long>( imageSize ) / sizeof(aiTexel)];
     pFile->Seek(0, aiOrigin_SET);
     pFile->Seek(0, aiOrigin_SET);
-    pFile->Read(reinterpret_cast<char*>(imageContent), imageSize, 1);
+    pFile->Read(reinterpret_cast<char*>(imageContent), static_cast<size_t>(imageSize), 1);
     mIOHandler->Close(pFile);
     mIOHandler->Close(pFile);
 
 
     // Enlarging the textures table
     // Enlarging the textures table

+ 4 - 0
code/res/assimp.rc

@@ -1,5 +1,9 @@
 #include "revision.h"
 #include "revision.h"
+#ifdef __GNUC__
+#include "winresrc.h"
+#else
 #include "winres.h"
 #include "winres.h"
+#endif
 
 
 LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
 LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
 #pragma code_page(1252)
 #pragma code_page(1252)

+ 4 - 4
contrib/unzip/crypt.c

@@ -43,10 +43,10 @@
 
 
 #include "crypt.h"
 #include "crypt.h"
 
 
-#ifdef _WIN32
+#ifdef _MSC_VER
 #   pragma warning(push)
 #   pragma warning(push)
 #   pragma warning(disable : 4244)
 #   pragma warning(disable : 4244)
-#endif // _WIN32
+#endif // _MSC_VER
 
 
 /***************************************************************************/
 /***************************************************************************/
 
 
@@ -164,8 +164,8 @@ int crypthead(const char *passwd, uint8_t *buf, int buf_size, uint32_t *pkeys,
     return n;
     return n;
 }
 }
 
 
-#ifdef _WIN32
+#ifdef _MSC_VER
 #   pragma warning(pop)
 #   pragma warning(pop)
-#endif // _WIN32
+#endif // _MSC_VER
 
 
 /***************************************************************************/
 /***************************************************************************/

+ 4 - 2
contrib/unzip/ioapi.c

@@ -23,8 +23,10 @@
 
 
 #ifdef _WIN32
 #ifdef _WIN32
 #   define snprintf _snprintf
 #   define snprintf _snprintf
+#ifdef _MSC_VER
 #   pragma warning(push)
 #   pragma warning(push)
 #   pragma warning(disable : 4131 4100)
 #   pragma warning(disable : 4131 4100)
+#endif
 #   ifdef __clang__
 #   ifdef __clang__
 #       pragma clang diagnostic push
 #       pragma clang diagnostic push
 #       pragma clang diagnostic ignored "-Wunused-parameter"
 #       pragma clang diagnostic ignored "-Wunused-parameter"
@@ -357,9 +359,9 @@ void fill_fopen64_filefunc(zlib_filefunc64_def *pzlib_filefunc_def)
     pzlib_filefunc_def->opaque = NULL;
     pzlib_filefunc_def->opaque = NULL;
 }
 }
 
 
-#ifdef _WIN32
+#ifdef _MSC_VER
 #   pragma warning(pop)
 #   pragma warning(pop)
 #   ifdef __clang__
 #   ifdef __clang__
 #       pragma clang diagnostic pop
 #       pragma clang diagnostic pop
 #   endif
 #   endif
-#endif // _WIN32
+#endif // _MSC_VER

+ 4 - 4
contrib/unzip/unzip.c

@@ -73,10 +73,10 @@
 #  define TRYFREE(p) {if (p) free(p);}
 #  define TRYFREE(p) {if (p) free(p);}
 #endif
 #endif
 
 
-#ifdef _WIN32
+#ifdef _MSC_VER
 #   pragma warning(push)
 #   pragma warning(push)
 #   pragma warning(disable : 4131 4244 4189 4245)
 #   pragma warning(disable : 4131 4244 4189 4245)
-#endif // _WIN32
+#endif // _MSC_VER
 
 
 const char unz_copyright[] =
 const char unz_copyright[] =
    " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
    " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
@@ -1995,6 +1995,6 @@ extern int ZEXPORT unzEndOfFile(unzFile file)
     return 0;
     return 0;
 }
 }
 
 
-#ifdef _WIN32
+#ifdef _MSC_VER
 #   pragma warning(pop)
 #   pragma warning(pop)
-#endif // _WIN32
+#endif // _MSC_VER

+ 273 - 191
include/assimp/XmlParser.h

@@ -99,295 +99,129 @@ template <class TNodeType>
 class TXmlParser {
 class TXmlParser {
 public:
 public:
     /// @brief The default class constructor.
     /// @brief The default class constructor.
-    TXmlParser() :
-            mDoc(nullptr),
-            mData() {
-        // empty
-    }
+    TXmlParser();
 
 
     ///	@brief  The class destructor.
     ///	@brief  The class destructor.
-    ~TXmlParser() {
-        clear();
-    }
+    ~TXmlParser();
 
 
     ///	@brief  Will clear the parsed xml-file.
     ///	@brief  Will clear the parsed xml-file.
-    void clear() {
-        if (mData.empty()) {
-            mDoc = nullptr;
-            return;
-        }
-        mData.clear();
-        delete mDoc;
-        mDoc = nullptr;
-    }
+    void clear();
 
 
     ///	@brief  Will search for a child-node by its name
     ///	@brief  Will search for a child-node by its name
     /// @param  name     [in] The name of the child-node.
     /// @param  name     [in] The name of the child-node.
     /// @return The node instance or nullptr, if nothing was found.
     /// @return The node instance or nullptr, if nothing was found.
-    TNodeType *findNode(const std::string &name) {
-        if (name.empty()) {
-            return nullptr;
-        }
-
-        if (nullptr == mDoc) {
-            return nullptr;
-        }
-
-        find_node_by_name_predicate predicate(name);
-        mCurrent = mDoc->find_node(predicate);
-        if (mCurrent.empty()) {
-            return nullptr;
-        }
-
-        return &mCurrent;
-    }
+    TNodeType *findNode(const std::string &name);
 
 
     /// @brief  Will return true, if the node is a child-node.
     /// @brief  Will return true, if the node is a child-node.
     /// @param  name    [in] The name of the child node to look for.
     /// @param  name    [in] The name of the child node to look for.
     /// @return true, if the node is a child-node or false if not.
     /// @return true, if the node is a child-node or false if not.
-    bool hasNode(const std::string &name) {
-        return nullptr != findNode(name);
-    }
+    bool hasNode(const std::string &name);
 
 
     /// @brief  Will parse an xml-file from a given stream.
     /// @brief  Will parse an xml-file from a given stream.
     /// @param  stream      The input stream.
     /// @param  stream      The input stream.
     /// @return true, if the parsing was successful, false if not.
     /// @return true, if the parsing was successful, false if not.
-    bool parse(IOStream *stream) {
-        if (nullptr == stream) {
-            ASSIMP_LOG_DEBUG("Stream is nullptr.");
-            return false;
-        }
-
-        const size_t len = stream->FileSize();
-        mData.resize(len + 1);
-        memset(&mData[0], '\0', len + 1);
-        stream->Read(&mData[0], 1, len);
+    bool parse(IOStream *stream);
 
 
-        mDoc = new pugi::xml_document();
-        pugi::xml_parse_result parse_result = mDoc->load_string(&mData[0], pugi::parse_full);
-        if (parse_result.status == pugi::status_ok) {
-            return true;
-        }
-
-        ASSIMP_LOG_DEBUG("Error while parse xml.", std::string(parse_result.description()), " @ ", parse_result.offset);
-
-        return false;
-    }
-
-    /// @brief  Will return truem if a root node is there.
+    /// @brief  Will return true if a root node is there.
     /// @return true in case of an existing root.
     /// @return true in case of an existing root.
-    bool hasRoot() const {
-        return nullptr != mDoc;
-    }
+    bool hasRoot() const;
+
     /// @brief  Will return the document pointer, is nullptr if no xml-file was parsed.
     /// @brief  Will return the document pointer, is nullptr if no xml-file was parsed.
     /// @return The pointer showing to the document.
     /// @return The pointer showing to the document.
-    pugi::xml_document *getDocument() const {
-        return mDoc;
-    }
+    pugi::xml_document *getDocument() const;
 
 
     /// @brief  Will return the root node, const version.
     /// @brief  Will return the root node, const version.
     /// @return The root node.
     /// @return The root node.
-    const TNodeType getRootNode() const {
-        static pugi::xml_node none;
-        if (nullptr == mDoc) {
-            return none;
-        }
-        return mDoc->root();
-    }
+    const TNodeType getRootNode() const;
 
 
     /// @brief  Will return the root node, non-const version.
     /// @brief  Will return the root node, non-const version.
     /// @return The root node.
     /// @return The root node.
-    TNodeType getRootNode() {
-        static pugi::xml_node none;
-        if (nullptr == mDoc) {
-            return none;
-        }
-        return mDoc->root();
-    }
+    TNodeType getRootNode();
 
 
     /// @brief Will check if a node with the given name is in.
     /// @brief Will check if a node with the given name is in.
     /// @param node     [in] The node to look in.
     /// @param node     [in] The node to look in.
     /// @param name     [in] The name of the child-node.
     /// @param name     [in] The name of the child-node.
     /// @return true, if node was found, false if not.
     /// @return true, if node was found, false if not.
-    static inline bool hasNode(XmlNode &node, const char *name) {
-        pugi::xml_node child = node.find_child(find_node_by_name_predicate(name));
-        return !child.empty();
-    }
+    static inline bool hasNode(XmlNode &node, const char *name);
 
 
     /// @brief Will check if an attribute is part of the XmlNode.
     /// @brief Will check if an attribute is part of the XmlNode.
     /// @param xmlNode  [in] The node to search in.
     /// @param xmlNode  [in] The node to search in.
     /// @param name     [in} The attribute name to look for.
     /// @param name     [in} The attribute name to look for.
     /// @return true, if the was found, false if not.
     /// @return true, if the was found, false if not.
-    static inline bool hasAttribute(XmlNode &xmlNode, const char *name) {
-        pugi::xml_attribute attr = xmlNode.attribute(name);
-        return !attr.empty();
-    }
+    static inline bool hasAttribute(XmlNode &xmlNode, const char *name);
 
 
     /// @brief Will try to get an unsigned int attribute value.
     /// @brief Will try to get an unsigned int attribute value.
     /// @param xmlNode  [in] The node to search in.
     /// @param xmlNode  [in] The node to search in.
     /// @param name     [in] The attribute name to look for.
     /// @param name     [in] The attribute name to look for.
     /// @param val      [out] The unsigned int value from the attribute.
     /// @param val      [out] The unsigned int value from the attribute.
     /// @return true, if the node contains an attribute with the given name and if the value is an unsigned int.
     /// @return true, if the node contains an attribute with the given name and if the value is an unsigned int.
-    static inline bool getUIntAttribute(XmlNode &xmlNode, const char *name, unsigned int &val) {
-        pugi::xml_attribute attr = xmlNode.attribute(name);
-        if (attr.empty()) {
-            return false;
-        }
-
-        val = attr.as_uint();
-        return true;
-    }
+    static inline bool getUIntAttribute(XmlNode &xmlNode, const char *name, unsigned int &val);
 
 
     /// @brief Will try to get an int attribute value.
     /// @brief Will try to get an int attribute value.
     /// @param xmlNode  [in] The node to search in.
     /// @param xmlNode  [in] The node to search in.
     /// @param name     [in] The attribute name to look for.
     /// @param name     [in] The attribute name to look for.
     /// @param val      [out] The int value from the attribute.
     /// @param val      [out] The int value from the attribute.
     /// @return true, if the node contains an attribute with the given name and if the value is an int.
     /// @return true, if the node contains an attribute with the given name and if the value is an int.
-    static inline bool getIntAttribute(XmlNode &xmlNode, const char *name, int &val) {
-        pugi::xml_attribute attr = xmlNode.attribute(name);
-        if (attr.empty()) {
-            return false;
-        }
-
-        val = attr.as_int();
-        return true;
-    }
+    static inline bool getIntAttribute(XmlNode &xmlNode, const char *name, int &val);
 
 
     /// @brief Will try to get a real attribute value.
     /// @brief Will try to get a real attribute value.
     /// @param xmlNode  [in] The node to search in.
     /// @param xmlNode  [in] The node to search in.
     /// @param name     [in] The attribute name to look for.
     /// @param name     [in] The attribute name to look for.
     /// @param val      [out] The real value from the attribute.
     /// @param val      [out] The real value from the attribute.
     /// @return true, if the node contains an attribute with the given name and if the value is a real.
     /// @return true, if the node contains an attribute with the given name and if the value is a real.
-    static inline bool getRealAttribute(XmlNode &xmlNode, const char *name, ai_real &val) {
-        pugi::xml_attribute attr = xmlNode.attribute(name);
-        if (attr.empty()) {
-            return false;
-        }
-#ifdef ASSIMP_DOUBLE_PRECISION
-        val = attr.as_double();
-#else
-        val = attr.as_float();
-#endif
-        return true;
-    }
+    static inline bool getRealAttribute(XmlNode &xmlNode, const char *name, ai_real &val);
 
 
     /// @brief Will try to get a float attribute value.
     /// @brief Will try to get a float attribute value.
     /// @param xmlNode  [in] The node to search in.
     /// @param xmlNode  [in] The node to search in.
     /// @param name     [in] The attribute name to look for.
     /// @param name     [in] The attribute name to look for.
     /// @param val      [out] The float value from the attribute.
     /// @param val      [out] The float value from the attribute.
     /// @return true, if the node contains an attribute with the given name and if the value is a float.
     /// @return true, if the node contains an attribute with the given name and if the value is a float.
-    static inline bool getFloatAttribute(XmlNode &xmlNode, const char *name, float &val) {
-        pugi::xml_attribute attr = xmlNode.attribute(name);
-        if (attr.empty()) {
-            return false;
-        }
-
-        val = attr.as_float();
-        return true;
-    }
+    static inline bool getFloatAttribute(XmlNode &xmlNode, const char *name, float &val);
 
 
     /// @brief Will try to get a double attribute value.
     /// @brief Will try to get a double attribute value.
     /// @param xmlNode  [in] The node to search in.
     /// @param xmlNode  [in] The node to search in.
     /// @param name     [in] The attribute name to look for.
     /// @param name     [in] The attribute name to look for.
     /// @param val      [out] The double value from the attribute.
     /// @param val      [out] The double value from the attribute.
     /// @return true, if the node contains an attribute with the given name and if the value is a double.
     /// @return true, if the node contains an attribute with the given name and if the value is a double.
-    static inline bool getDoubleAttribute(XmlNode &xmlNode, const char *name, double &val) {
-        pugi::xml_attribute attr = xmlNode.attribute(name);
-        if (attr.empty()) {
-            return false;
-        }
-
-        val = attr.as_double();
-        return true;
-    }
+    static inline bool getDoubleAttribute(XmlNode &xmlNode, const char *name, double &val);
 
 
     /// @brief Will try to get a std::string attribute value.
     /// @brief Will try to get a std::string attribute value.
     /// @param xmlNode  [in] The node to search in.
     /// @param xmlNode  [in] The node to search in.
     /// @param name     [in] The attribute name to look for.
     /// @param name     [in] The attribute name to look for.
     /// @param val      [out] The std::string value from the attribute.
     /// @param val      [out] The std::string value from the attribute.
     /// @return true, if the node contains an attribute with the given name and if the value is a std::string.
     /// @return true, if the node contains an attribute with the given name and if the value is a std::string.
-    static inline bool getStdStrAttribute(XmlNode &xmlNode, const char *name, std::string &val) {
-        pugi::xml_attribute attr = xmlNode.attribute(name);
-        if (attr.empty()) {
-            return false;
-        }
-
-        val = attr.as_string();
-        return true;
-    }
+    static inline bool getStdStrAttribute(XmlNode &xmlNode, const char *name, std::string &val);
 
 
     /// @brief Will try to get a bool attribute value.
     /// @brief Will try to get a bool attribute value.
     /// @param xmlNode  [in] The node to search in.
     /// @param xmlNode  [in] The node to search in.
     /// @param name     [in] The attribute name to look for.
     /// @param name     [in] The attribute name to look for.
     /// @param val      [out] The bool value from the attribute.
     /// @param val      [out] The bool value from the attribute.
     /// @return true, if the node contains an attribute with the given name and if the value is a bool.
     /// @return true, if the node contains an attribute with the given name and if the value is a bool.
-    static inline bool getBoolAttribute(XmlNode &xmlNode, const char *name, bool &val) {
-        pugi::xml_attribute attr = xmlNode.attribute(name);
-        if (attr.empty()) {
-            return false;
-        }
-
-        val = attr.as_bool();
-        return true;
-    }
+    static inline bool getBoolAttribute(XmlNode &xmlNode, const char *name, bool &val);
 
 
     /// @brief Will try to get the value of the node as a string.
     /// @brief Will try to get the value of the node as a string.
     /// @param node     [in] The node to search in.
     /// @param node     [in] The node to search in.
     /// @param text     [out] The value as a text.
     /// @param text     [out] The value as a text.
     /// @return true, if the value can be read out.
     /// @return true, if the value can be read out.
-    static inline bool getValueAsString(XmlNode &node, std::string &text) {
-        text = std::string();
-        if (node.empty()) {
-            return false;
-        }
-
-        text = node.text().as_string();
-
-        return true;
-    }
+    static inline bool getValueAsString(XmlNode &node, std::string &text);
 
 
     /// @brief Will try to get the value of the node as a float.
     /// @brief Will try to get the value of the node as a float.
     /// @param node     [in] The node to search in.
     /// @param node     [in] The node to search in.
     /// @param text     [out] The value as a float.
     /// @param text     [out] The value as a float.
     /// @return true, if the value can be read out.
     /// @return true, if the value can be read out.
-    static inline bool getValueAsFloat(XmlNode &node, ai_real &v) {
-        if (node.empty()) {
-            return false;
-        }
-
-        v = node.text().as_float();
-
-        return true;
-    }
+    static inline bool getValueAsFloat(XmlNode &node, ai_real &v);
 
 
     /// @brief Will try to get the value of the node as an integer.
     /// @brief Will try to get the value of the node as an integer.
     /// @param node     [in] The node to search in.
     /// @param node     [in] The node to search in.
     /// @param text     [out] The value as a int.
     /// @param text     [out] The value as a int.
     /// @return true, if the value can be read out.
     /// @return true, if the value can be read out.
-    static inline bool getValueAsInt(XmlNode &node, int &v) {
-        if (node.empty()) {
-            return false;
-        }
-
-        v = node.text().as_int();
-
-        return true;
-    }
+    static inline bool getValueAsInt(XmlNode &node, int &v);
 
 
     /// @brief Will try to get the value of the node as an bool.
     /// @brief Will try to get the value of the node as an bool.
     /// @param node     [in] The node to search in.
     /// @param node     [in] The node to search in.
     /// @param text     [out] The value as a bool.
     /// @param text     [out] The value as a bool.
     /// @return true, if the value can be read out.
     /// @return true, if the value can be read out.
-    static inline bool getValueAsBool(XmlNode& node, bool& v)
-    {
-        if (node.empty()) {
-            return false;
-        }
-
-        v = node.text().as_bool();
-
-        return true;
-    }
+    static inline bool getValueAsBool(XmlNode &node, bool &v);
 
 
 private:
 private:
     pugi::xml_document *mDoc;
     pugi::xml_document *mDoc;
@@ -395,6 +229,254 @@ private:
     std::vector<char> mData;
     std::vector<char> mData;
 };
 };
 
 
+template <class TNodeType>
+inline TXmlParser<TNodeType>::TXmlParser() :
+        mDoc(nullptr),
+        mData() {
+    // empty
+}
+
+template <class TNodeType>
+inline TXmlParser<TNodeType>::~TXmlParser() {
+    clear();
+}
+
+template <class TNodeType>
+inline void TXmlParser<TNodeType>::clear() {
+    if (mData.empty()) {
+        if (mDoc) {
+            delete mDoc;
+        }
+        mDoc = nullptr;
+        return;
+    }
+
+    mData.clear();
+    delete mDoc;
+    mDoc = nullptr;
+}
+
+template <class TNodeType>
+inline TNodeType *TXmlParser<TNodeType>::findNode(const std::string &name) {
+    if (name.empty()) {
+        return nullptr;
+    }
+
+    if (nullptr == mDoc) {
+        return nullptr;
+    }
+
+    find_node_by_name_predicate predicate(name);
+    mCurrent = mDoc->find_node(predicate);
+    if (mCurrent.empty()) {
+        return nullptr;
+    }
+
+    return &mCurrent;
+}
+
+template <class TNodeType>
+bool TXmlParser<TNodeType>::hasNode(const std::string &name) {
+    return nullptr != findNode(name);
+}
+
+template <class TNodeType>
+bool TXmlParser<TNodeType>::parse(IOStream *stream) {
+    if (hasRoot()) {
+        clear();
+    }
+
+    if (nullptr == stream) {
+        ASSIMP_LOG_DEBUG("Stream is nullptr.");
+        return false;
+    }
+
+    const size_t len = stream->FileSize();
+    mData.resize(len + 1);
+    memset(&mData[0], '\0', len + 1);
+    stream->Read(&mData[0], 1, len);
+
+    mDoc = new pugi::xml_document();
+    pugi::xml_parse_result parse_result = mDoc->load_string(&mData[0], pugi::parse_full);
+    if (parse_result.status == pugi::status_ok) {
+        return true;
+    }
+
+    ASSIMP_LOG_DEBUG("Error while parse xml.", std::string(parse_result.description()), " @ ", parse_result.offset);
+
+    return false;
+}
+
+template <class TNodeType>
+bool TXmlParser<TNodeType>::hasRoot() const {
+    return nullptr != mDoc;
+}
+
+template <class TNodeType>
+pugi::xml_document *TXmlParser<TNodeType>::getDocument() const {
+    return mDoc;
+}
+
+template <class TNodeType>
+const TNodeType TXmlParser<TNodeType>::getRootNode() const {
+    static pugi::xml_node none;
+    if (nullptr == mDoc) {
+        return none;
+    }
+    return mDoc->root();
+}
+
+template <class TNodeType>
+TNodeType TXmlParser<TNodeType>::getRootNode() {
+    static pugi::xml_node none;
+    if (nullptr == mDoc) {
+        return none;
+    }
+
+    return mDoc->root();
+}
+
+template <class TNodeType>
+inline bool TXmlParser<TNodeType>::hasNode(XmlNode &node, const char *name) {
+    pugi::xml_node child = node.find_child(find_node_by_name_predicate(name));
+    return !child.empty();
+}
+
+template <class TNodeType>
+inline bool TXmlParser<TNodeType>::hasAttribute(XmlNode &xmlNode, const char *name) {
+    pugi::xml_attribute attr = xmlNode.attribute(name);
+    return !attr.empty();
+}
+
+template <class TNodeType>
+inline bool TXmlParser<TNodeType>::getUIntAttribute(XmlNode &xmlNode, const char *name, unsigned int &val) {
+    pugi::xml_attribute attr = xmlNode.attribute(name);
+    if (attr.empty()) {
+        return false;
+    }
+
+    val = attr.as_uint();
+    return true;
+}
+
+template <class TNodeType>
+inline bool TXmlParser<TNodeType>::getIntAttribute(XmlNode &xmlNode, const char *name, int &val) {
+    pugi::xml_attribute attr = xmlNode.attribute(name);
+    if (attr.empty()) {
+        return false;
+    }
+
+    val = attr.as_int();
+    return true;
+}
+
+template <class TNodeType>
+inline bool TXmlParser<TNodeType>::getRealAttribute(XmlNode &xmlNode, const char *name, ai_real &val) {
+    pugi::xml_attribute attr = xmlNode.attribute(name);
+    if (attr.empty()) {
+        return false;
+    }
+#ifdef ASSIMP_DOUBLE_PRECISION
+    val = attr.as_double();
+#else
+    val = attr.as_float();
+#endif
+    return true;
+}
+
+template <class TNodeType>
+inline bool TXmlParser<TNodeType>::getFloatAttribute(XmlNode &xmlNode, const char *name, float &val) {
+    pugi::xml_attribute attr = xmlNode.attribute(name);
+    if (attr.empty()) {
+        return false;
+    }
+
+    val = attr.as_float();
+
+    return true;
+}
+
+template <class TNodeType>
+inline bool TXmlParser<TNodeType>::getDoubleAttribute(XmlNode &xmlNode, const char *name, double &val) {
+    pugi::xml_attribute attr = xmlNode.attribute(name);
+    if (attr.empty()) {
+        return false;
+    }
+
+    val = attr.as_double();
+
+    return true;
+}
+
+template <class TNodeType>
+inline bool TXmlParser<TNodeType>::getStdStrAttribute(XmlNode &xmlNode, const char *name, std::string &val) {
+    pugi::xml_attribute attr = xmlNode.attribute(name);
+    if (attr.empty()) {
+        return false;
+    }
+
+    val = attr.as_string();
+
+    return true;
+}
+
+template <class TNodeType>
+inline bool TXmlParser<TNodeType>::getBoolAttribute(XmlNode &xmlNode, const char *name, bool &val) {
+    pugi::xml_attribute attr = xmlNode.attribute(name);
+    if (attr.empty()) {
+        return false;
+    }
+
+    val = attr.as_bool();
+
+    return true;
+}
+
+template <class TNodeType>
+inline bool TXmlParser<TNodeType>::getValueAsString(XmlNode &node, std::string &text) {
+    text = std::string();
+    if (node.empty()) {
+        return false;
+    }
+
+    text = node.text().as_string();
+
+    return true;
+}
+
+template <class TNodeType>
+inline bool TXmlParser<TNodeType>::getValueAsFloat(XmlNode &node, ai_real &v) {
+    if (node.empty()) {
+        return false;
+    }
+
+    v = node.text().as_float();
+
+    return true;
+}
+
+template <class TNodeType>
+inline bool TXmlParser<TNodeType>::getValueAsInt(XmlNode &node, int &v) {
+    if (node.empty()) {
+        return false;
+    }
+
+    v = node.text().as_int();
+
+    return true;
+}
+
+template <class TNodeType>
+inline bool TXmlParser<TNodeType>::getValueAsBool(XmlNode &node, bool &v) {
+    if (node.empty()) {
+        return false;
+    }
+
+    v = node.text().as_bool();
+
+    return true;
+}
+
 using XmlParser = TXmlParser<pugi::xml_node>;
 using XmlParser = TXmlParser<pugi::xml_node>;
 
 
 ///	@brief  This class declares an iterator to loop through all children of the root node.
 ///	@brief  This class declares an iterator to loop through all children of the root node.

+ 9 - 0
include/assimp/config.h.in

@@ -691,6 +691,15 @@ enum aiComponent
 #define AI_CONFIG_FBX_CONVERT_TO_M \
 #define AI_CONFIG_FBX_CONVERT_TO_M \
     "AI_CONFIG_FBX_CONVERT_TO_M"
     "AI_CONFIG_FBX_CONVERT_TO_M"
 
 
+// ---------------------------------------------------------------------------
+/** @brief  Will enable the skeleton structo to store bone data.
+ *
+ *  This will decouple the bone coupling to the mesh. This feature is
+ *  experimental.
+ */
+#define AI_CONFIG_FBX_USE_SKELETON_BONE_CONTAINER \
+    "AI_CONFIG_FBX_USE_SKELETON_BONE_CONTAINER"
+
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** @brief  Set the vertex animation keyframe to be imported
 /** @brief  Set the vertex animation keyframe to be imported
  *
  *

+ 120 - 21
include/assimp/mesh.h

@@ -120,7 +120,7 @@ extern "C" {
  * primitive are actually present in a mesh. The #aiProcess_SortByPType flag
  * primitive are actually present in a mesh. The #aiProcess_SortByPType flag
  * executes a special post-processing algorithm which splits meshes with
  * executes a special post-processing algorithm which splits meshes with
  * *different* primitive types mixed up (e.g. lines and triangles) in several
  * *different* primitive types mixed up (e.g. lines and triangles) in several
- * 'clean' submeshes. Furthermore there is a configuration option (
+ * 'clean' sub-meshes. Furthermore there is a configuration option (
  * #AI_CONFIG_PP_SBP_REMOVE) to force #aiProcess_SortByPType to remove
  * #AI_CONFIG_PP_SBP_REMOVE) to force #aiProcess_SortByPType to remove
  * specific kinds of primitives from the imported scene, completely and forever.
  * specific kinds of primitives from the imported scene, completely and forever.
  * In many cases you'll probably want to set this setting to
  * In many cases you'll probably want to set this setting to
@@ -269,12 +269,12 @@ struct aiBone {
     unsigned int mNumWeights;
     unsigned int mNumWeights;
 
 
 #ifndef ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS
 #ifndef ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS
-    // The bone armature node - used for skeleton conversion
-    // you must enable aiProcess_PopulateArmatureData to populate this
+    /// The bone armature node - used for skeleton conversion
+    /// you must enable aiProcess_PopulateArmatureData to populate this
     C_STRUCT aiNode *mArmature;
     C_STRUCT aiNode *mArmature;
 
 
-    // The bone node in the scene - used for skeleton conversion
-    // you must enable aiProcess_PopulateArmatureData to populate this
+    /// The bone node in the scene - used for skeleton conversion
+    /// you must enable aiProcess_PopulateArmatureData to populate this
     C_STRUCT aiNode *mNode;
     C_STRUCT aiNode *mNode;
 
 
 #endif
 #endif
@@ -296,7 +296,7 @@ struct aiBone {
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 
 
-    //! Default constructor
+    ///	@brief  Default constructor
     aiBone() AI_NO_EXCEPT
     aiBone() AI_NO_EXCEPT
             : mName(),
             : mName(),
               mNumWeights(0),
               mNumWeights(0),
@@ -309,7 +309,7 @@ struct aiBone {
         // empty
         // empty
     }
     }
 
 
-    //! Copy constructor
+    /// @brief  Copy constructor
     aiBone(const aiBone &other) :
     aiBone(const aiBone &other) :
             mName(other.mName),
             mName(other.mName),
             mNumWeights(other.mNumWeights),
             mNumWeights(other.mNumWeights),
@@ -319,14 +319,27 @@ struct aiBone {
 #endif
 #endif
             mWeights(nullptr),
             mWeights(nullptr),
             mOffsetMatrix(other.mOffsetMatrix) {
             mOffsetMatrix(other.mOffsetMatrix) {
-        if (other.mWeights && other.mNumWeights) {
-            mWeights = new aiVertexWeight[mNumWeights];
-            ::memcpy(mWeights, other.mWeights, mNumWeights * sizeof(aiVertexWeight));
+        copyVertexWeights(other);
+    }
+
+    void copyVertexWeights( const aiBone &other ) {
+        if (other.mWeights == nullptr || other.mNumWeights == 0) {
+            mWeights = nullptr;
+            mNumWeights = 0;
+            return;
+        }
+
+        mNumWeights = other.mNumWeights;
+        if (mWeights) {
+            delete[] mWeights;
         }
         }
+
+        mWeights = new aiVertexWeight[mNumWeights];
+        ::memcpy(mWeights, other.mWeights, mNumWeights * sizeof(aiVertexWeight));
     }
     }
 
 
     //! Assignment operator
     //! Assignment operator
-    aiBone &operator=(const aiBone &other) {
+    aiBone &operator = (const aiBone &other) {
         if (this == &other) {
         if (this == &other) {
             return *this;
             return *this;
         }
         }
@@ -334,21 +347,13 @@ struct aiBone {
         mName = other.mName;
         mName = other.mName;
         mNumWeights = other.mNumWeights;
         mNumWeights = other.mNumWeights;
         mOffsetMatrix = other.mOffsetMatrix;
         mOffsetMatrix = other.mOffsetMatrix;
-
-        if (other.mWeights && other.mNumWeights) {
-            if (mWeights) {
-                delete[] mWeights;
-            }
-
-            mWeights = new aiVertexWeight[mNumWeights];
-            ::memcpy(mWeights, other.mWeights, mNumWeights * sizeof(aiVertexWeight));
-        }
+        copyVertexWeights(other);
 
 
         return *this;
         return *this;
     }
     }
 
 
     bool operator==(const aiBone &rhs) const {
     bool operator==(const aiBone &rhs) const {
-        if (mName != rhs.mName || mNumWeights != rhs.mNumWeights) {
+        if (mName != rhs.mName || mNumWeights != rhs.mNumWeights ) {
             return false;
             return false;
         }
         }
 
 
@@ -937,6 +942,100 @@ struct aiMesh {
 #endif // __cplusplus
 #endif // __cplusplus
 };
 };
 
 
+struct aiSkeletonBone {
+    /// The parent bone index, is -1 one if this bone represents the root bone.
+    int mParent;
+
+
+#ifndef ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS
+    /// The bone armature node - used for skeleton conversion
+    /// you must enable aiProcess_PopulateArmatureData to populate this
+    C_STRUCT aiNode *mArmature;
+
+    /// The bone node in the scene - used for skeleton conversion
+    /// you must enable aiProcess_PopulateArmatureData to populate this
+    C_STRUCT aiNode *mNode;
+
+#endif
+    /// @brief The number of weights
+    unsigned int mNumnWeights;
+
+    /// The mesh index, which will get influenced by the weight.
+    C_STRUCT aiMesh *mMeshId;
+
+    /// The influence weights of this bone, by vertex index.
+    C_STRUCT aiVertexWeight *mWeights;
+
+    /** Matrix that transforms from bone space to mesh space in bind pose.
+     *
+     * This matrix describes the position of the mesh
+     * in the local space of this bone when the skeleton was bound.
+     * Thus it can be used directly to determine a desired vertex position,
+     * given the world-space transform of the bone when animated,
+     * and the position of the vertex in mesh space.
+     *
+     * It is sometimes called an inverse-bind matrix,
+     * or inverse bind pose matrix.
+     */
+    C_STRUCT aiMatrix4x4 mOffsetMatrix;
+
+    /// Matrix that transforms the locale bone in bind pose.
+    C_STRUCT aiMatrix4x4 mLocalMatrix;
+
+#ifdef __cplusplus
+    aiSkeletonBone() :
+            mParent(-1),
+            mArmature(nullptr),
+            mNode(nullptr),
+            mNumnWeights(0),
+            mMeshId(nullptr),
+            mWeights(nullptr),
+            mOffsetMatrix(),
+            mLocalMatrix() {
+        // empty
+    }
+
+    ~aiSkeletonBone() {
+        delete[] mWeights;
+        mWeights = nullptr;
+    }
+#endif // __cplusplus
+};
+/**
+ *  @brief  
+ */
+struct aiSkeleton {
+    /**
+     *
+     */
+    C_STRUCT aiString mName;
+
+    /**
+     *
+     */
+    unsigned int mNumBones;
+
+    /**
+     *
+     */
+    C_STRUCT aiSkeletonBone **mBones;
+
+#ifdef __cplusplus
+    /**
+     *
+     */
+    aiSkeleton() AI_NO_EXCEPT : mName(), mNumBones(0), mBones(nullptr) {
+        // empty
+    }
+
+    /**
+     *
+     */
+    ~aiSkeleton() {
+        delete[] mBones;
+    }
+#endif // __cplusplus
+};
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif //! extern "C"
 #endif //! extern "C"

+ 15 - 2
include/assimp/scene.h

@@ -79,8 +79,7 @@ extern "C" {
  * the imported scene does consist of only a single root node without children.
  * the imported scene does consist of only a single root node without children.
  */
  */
 // -------------------------------------------------------------------------------
 // -------------------------------------------------------------------------------
-struct ASSIMP_API aiNode
-{
+struct ASSIMP_API aiNode {
     /** The name of the node.
     /** The name of the node.
      *
      *
      * The name might be empty (length of zero) but all nodes which
      * The name might be empty (length of zero) but all nodes which
@@ -343,6 +342,16 @@ struct aiScene
      */
      */
     C_STRUCT aiString mName;
     C_STRUCT aiString mName;
 
 
+    /**
+     *
+     */
+    unsigned int mNumSkeletons;
+
+    /**
+     *
+     */
+    C_STRUCT aiSkeleton **mSkeletons;
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 
 
     //! Default constructor - set everything to 0/nullptr
     //! Default constructor - set everything to 0/nullptr
@@ -383,6 +392,10 @@ struct aiScene
         return mAnimations != nullptr && mNumAnimations > 0;
         return mAnimations != nullptr && mNumAnimations > 0;
     }
     }
 
 
+    bool hasSkeletons() const {
+        return mSkeletons != nullptr && mNumSkeletons > 0;
+    }
+
     //! Returns a short filename from a full path
     //! Returns a short filename from a full path
     static const char* GetShortFilename(const char* filename) {
     static const char* GetShortFilename(const char* filename) {
         const char* lastSlash = strrchr(filename, '/');
         const char* lastSlash = strrchr(filename, '/');

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 132 - 0
test/models/Collada/box_nested_animation.dae


BIN
test/models/FBX/animation_with_skeleton.fbx


+ 38 - 25
test/unit/utColladaImportExport.cpp

@@ -69,31 +69,44 @@ public:
 
 
     virtual bool importerTest() final {
     virtual bool importerTest() final {
         Assimp::Importer importer;
         Assimp::Importer importer;
-        const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/duck.dae", aiProcess_ValidateDataStructure);
-        if (scene == nullptr)
-            return false;
-
-        // Expected number of items
-        EXPECT_EQ(scene->mNumMeshes, 1u);
-        EXPECT_EQ(scene->mNumMaterials, 1u);
-        EXPECT_EQ(scene->mNumAnimations, 0u);
-        EXPECT_EQ(scene->mNumTextures, 0u);
-        EXPECT_EQ(scene->mNumLights, 1u);
-        EXPECT_EQ(scene->mNumCameras, 1u);
-
-        // Expected common metadata
-        aiString value;
-        EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, value)) << "No importer format metadata";
-        EXPECT_STREQ("Collada Importer", value.C_Str());
-
-        EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, value)) << "No format version metadata";
-        EXPECT_STREQ("1.4.1", value.C_Str());
-
-        EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_GENERATOR, value)) << "No generator metadata";
-        EXPECT_EQ(strncmp(value.C_Str(), "Maya 8.0", 8), 0) << "AI_METADATA_SOURCE_GENERATOR was: " << value.C_Str();
-
-        EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_COPYRIGHT, value)) << "No copyright metadata";
-        EXPECT_EQ(strncmp(value.C_Str(), "Copyright 2006", 14), 0) << "AI_METADATA_SOURCE_COPYRIGHT was: " << value.C_Str();
+        {
+          const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/duck.dae", aiProcess_ValidateDataStructure);
+          if (scene == nullptr)
+              return false;
+
+          // Expected number of items
+          EXPECT_EQ(scene->mNumMeshes, 1u);
+          EXPECT_EQ(scene->mNumMaterials, 1u);
+          EXPECT_EQ(scene->mNumAnimations, 0u);
+          EXPECT_EQ(scene->mNumTextures, 0u);
+          EXPECT_EQ(scene->mNumLights, 1u);
+          EXPECT_EQ(scene->mNumCameras, 1u);
+
+          // Expected common metadata
+          aiString value;
+          EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, value)) << "No importer format metadata";
+          EXPECT_STREQ("Collada Importer", value.C_Str());
+
+          EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, value)) << "No format version metadata";
+          EXPECT_STREQ("1.4.1", value.C_Str());
+
+          EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_GENERATOR, value)) << "No generator metadata";
+          EXPECT_EQ(strncmp(value.C_Str(), "Maya 8.0", 8), 0) << "AI_METADATA_SOURCE_GENERATOR was: " << value.C_Str();
+
+          EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_COPYRIGHT, value)) << "No copyright metadata";
+          EXPECT_EQ(strncmp(value.C_Str(), "Copyright 2006", 14), 0) << "AI_METADATA_SOURCE_COPYRIGHT was: " << value.C_Str();
+        }
+
+        {
+          const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/box_nested_animation.dae", aiProcess_ValidateDataStructure);
+          if (scene == nullptr)
+              return false;
+
+          // Expect only one animation with the correct name
+          EXPECT_EQ(scene->mNumAnimations, 1u);
+          EXPECT_EQ(std::string(scene->mAnimations[0]->mName.C_Str()), std::string("Armature"));
+
+        }
 
 
         return true;
         return true;
     }
     }

+ 7 - 0
test/unit/utFBXImporterExporter.cpp

@@ -423,3 +423,10 @@ TEST_F(utFBXImporterExporter, importMaxPbrMaterialsSpecularGloss) {
     ASSERT_EQ(mat->Get("$raw.3dsMax|main|emit_color", aiTextureType_NONE, 0, emitColor), aiReturn_SUCCESS);
     ASSERT_EQ(mat->Get("$raw.3dsMax|main|emit_color", aiTextureType_NONE, 0, emitColor), aiReturn_SUCCESS);
     EXPECT_EQ(emitColor, aiColor4D(1, 0, 1, 1));
     EXPECT_EQ(emitColor, aiColor4D(1, 0, 1, 1));
 }
 }
+
+TEST_F(utFBXImporterExporter, importSkeletonTest) {
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/animation_with_skeleton.fbx", aiProcess_ValidateDataStructure);
+    ASSERT_NE(nullptr, scene);
+    ASSERT_TRUE(scene->mRootNode);
+}

+ 4 - 1
test/unit/utOpenGEXImportExport.cpp

@@ -40,6 +40,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 */
 
 
 #include "AbstractImportExportBase.h"
 #include "AbstractImportExportBase.h"
+
+#include <assimp/scene.h>
 #include "UnitTestPCH.h"
 #include "UnitTestPCH.h"
 
 
 #include <assimp/Importer.hpp>
 #include <assimp/Importer.hpp>
@@ -51,11 +53,12 @@ public:
     bool importerTest() override {
     bool importerTest() override {
         Assimp::Importer importer;
         Assimp::Importer importer;
         const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/OpenGEX/Example.ogex", 0);
         const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/OpenGEX/Example.ogex", 0);
+        EXPECT_EQ(1u, scene->mNumMeshes);
         return nullptr != scene;
         return nullptr != scene;
     }
     }
 };
 };
 
 
-TEST_F(utOpenGEXImportExport, importLWSFromFileTest) {
+TEST_F(utOpenGEXImportExport, importOpenGexFromFileTest) {
     EXPECT_TRUE(importerTest());
     EXPECT_TRUE(importerTest());
 }
 }
 
 

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác