Browse Source

Bugfix/cosmetic code cleanup (#5947)

* Refactorings: Code cleanups

* More cosmetic changes

---------

Co-authored-by: Kim Kulling <[email protected]>
Kim Kulling 7 months ago
parent
commit
5fa7b8ceb9
52 changed files with 608 additions and 649 deletions
  1. 1 2
      code/AssetLib/3DS/3DSExporter.h
  2. 1 2
      code/AssetLib/3DS/3DSLoader.h
  3. 1 1
      code/AssetLib/3MF/D3MFImporter.cpp
  4. 3 3
      code/AssetLib/3MF/XmlSerializer.cpp
  5. 3 3
      code/AssetLib/3MF/XmlSerializer.h
  6. 6 7
      code/AssetLib/AC/ACLoader.cpp
  7. 2 2
      code/AssetLib/AC/ACLoader.h
  8. 1 1
      code/AssetLib/AMF/AMFImporter.cpp
  9. 7 6
      code/AssetLib/AMF/AMFImporter_Node.hpp
  10. 1 1
      code/AssetLib/AMF/AMFImporter_Postprocess.cpp
  11. 3 7
      code/AssetLib/BVH/BVHLoader.cpp
  12. 7 12
      code/AssetLib/BVH/BVHLoader.h
  13. 0 1
      code/AssetLib/Blender/BlenderDNA.h
  14. 3 1
      code/AssetLib/Blender/BlenderDNA.inl
  15. 0 1
      code/AssetLib/Blender/BlenderIntermediate.h
  16. 2 10
      code/AssetLib/C4D/C4DImporter.cpp
  17. 2 2
      code/AssetLib/C4D/C4DImporter.h
  18. 1 1
      code/AssetLib/COB/COBLoader.cpp
  19. 4 8
      code/AssetLib/CSM/CSMLoader.cpp
  20. 310 252
      code/AssetLib/Collada/ColladaExporter.cpp
  21. 0 1
      code/AssetLib/Collada/ColladaExporter.h
  22. 1 0
      code/AssetLib/Collada/ColladaLoader.cpp
  23. 21 18
      code/AssetLib/Collada/ColladaLoader.h
  24. 8 19
      code/AssetLib/DXF/DXFHelper.h
  25. 22 20
      code/AssetLib/FBX/FBXAnimation.cpp
  26. 9 52
      code/AssetLib/FBX/FBXBinaryTokenizer.cpp
  27. 2 1
      code/AssetLib/FBX/FBXCommon.h
  28. 7 7
      code/AssetLib/FBX/FBXConverter.cpp
  29. 1 2
      code/AssetLib/FBX/FBXConverter.h
  30. 5 18
      code/AssetLib/FBX/FBXDeformer.cpp
  31. 8 0
      code/AssetLib/FBX/FBXDocument.cpp
  32. 7 7
      code/AssetLib/FBX/FBXDocument.h
  33. 10 15
      code/AssetLib/FBX/FBXDocumentUtil.cpp
  34. 1 1
      code/AssetLib/FBX/FBXDocumentUtil.h
  35. 13 20
      code/AssetLib/FBX/FBXExportNode.cpp
  36. 26 35
      code/AssetLib/FBX/FBXExportNode.h
  37. 14 36
      code/AssetLib/FBX/FBXExporter.cpp
  38. 0 1
      code/AssetLib/FBX/FBXExporter.h
  39. 0 4
      code/AssetLib/FBX/FBXMaterial.cpp
  40. 1 2
      code/AssetLib/FBX/FBXParser.h
  41. 4 16
      code/AssetLib/FBX/FBXProperties.cpp
  42. 20 25
      code/AssetLib/FBX/FBXProperties.h
  43. 0 5
      code/AssetLib/FBX/FBXUtil.h
  44. 0 1
      code/AssetLib/NFF/NFFLoader.h
  45. 1 1
      code/AssetLib/Obj/ObjExporter.cpp
  46. 2 2
      code/AssetLib/Obj/ObjExporter.h
  47. 1 1
      code/AssetLib/Obj/ObjFileImporter.h
  48. 1 5
      code/AssetLib/Obj/ObjFileMtlImporter.cpp
  49. 2 5
      code/AssetLib/Obj/ObjFileMtlImporter.h
  50. 2 2
      code/AssetLib/Obj/ObjFileParser.cpp
  51. 7 4
      code/AssetLib/Obj/ObjFileParser.h
  52. 54 0
      include/assimp/scene.h

+ 1 - 2
code/AssetLib/3DS/3DSExporter.h

@@ -63,7 +63,7 @@ namespace Assimp {
  *  @brief  Helper class to export a given scene to a 3DS file.
  */
 // ------------------------------------------------------------------------------------------------
-class Discreet3DSExporter {
+class Discreet3DSExporter final {
 public:
     Discreet3DSExporter(std::shared_ptr<IOStream> &outfile, const aiScene* pScene);
     ~Discreet3DSExporter() = default;
@@ -88,7 +88,6 @@ private:
 
     using MeshesByNodeMap = std::multimap<const aiNode*, unsigned int>;
     MeshesByNodeMap meshes;
-
 };
 
 } // Namespace Assimp

+ 1 - 2
code/AssetLib/3DS/3DSLoader.h

@@ -5,7 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2024, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -64,7 +63,7 @@ using namespace D3DS;
 // ---------------------------------------------------------------------------------
 /** Importer class for 3D Studio r3 and r4 3DS files
  */
-class Discreet3DSImporter : public BaseImporter {
+class Discreet3DSImporter final : public BaseImporter {
 public:
     Discreet3DSImporter();
     ~Discreet3DSImporter() override = default;

+ 1 - 1
code/AssetLib/3MF/D3MFImporter.cpp

@@ -107,7 +107,7 @@ void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene,
 
     XmlParser xmlParser;
     if (xmlParser.parse(opcPackage.RootStream())) {
-        XmlSerializer xmlSerializer(&xmlParser);
+        XmlSerializer xmlSerializer(xmlParser);
         xmlSerializer.ImportXml(pScene);
 
         const std::vector<aiTexture*> &tex =  opcPackage.GetEmbeddedTextures();

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

@@ -199,11 +199,11 @@ void assignDiffuseColor(XmlNode &node, aiMaterial *mat) {
 
 } // namespace
 
-XmlSerializer::XmlSerializer(XmlParser *xmlParser) :
+XmlSerializer::XmlSerializer(XmlParser &xmlParser) :
         mResourcesDictionnary(),
         mMeshCount(0),
         mXmlParser(xmlParser) {
-    ai_assert(nullptr != xmlParser);
+    // empty
 }
 
 XmlSerializer::~XmlSerializer() {
@@ -218,7 +218,7 @@ void XmlSerializer::ImportXml(aiScene *scene) {
     }
 
     scene->mRootNode = new aiNode(XmlTag::RootTag);
-    XmlNode node = mXmlParser->getRootNode().child(XmlTag::model);
+    XmlNode node = mXmlParser.getRootNode().child(XmlTag::model);
     if (node.empty()) {
         return;
     }

+ 3 - 3
code/AssetLib/3MF/XmlSerializer.h

@@ -59,9 +59,9 @@ class Texture2DGroup;
 class EmbeddedTexture;
 class ColorGroup;
 
-class XmlSerializer {
+class XmlSerializer final {
 public:
-    XmlSerializer(XmlParser *xmlParser);
+    XmlSerializer(XmlParser &xmlParser);
     ~XmlSerializer();
     void ImportXml(aiScene *scene);
 
@@ -92,7 +92,7 @@ private:
     std::vector<aiMaterial *> mMaterials;
     std::map<unsigned int, Resource *> mResourcesDictionnary;
     unsigned int mMeshCount;
-    XmlParser *mXmlParser;
+    XmlParser &mXmlParser;
 };
 
 } // namespace D3MF

+ 6 - 7
code/AssetLib/AC/ACLoader.cpp

@@ -144,10 +144,6 @@ AC3DImporter::AC3DImporter() :
     // nothing to be done here
 }
 
-// ------------------------------------------------------------------------------------------------
-// Destructor, private as well
-AC3DImporter::~AC3DImporter() = default;
-
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
 bool AC3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
@@ -171,8 +167,9 @@ bool AC3DImporter::GetNextLine() {
 // ------------------------------------------------------------------------------------------------
 // Parse an object section in an AC file
 bool AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
-    if (!TokenMatch(mBuffer.data, "OBJECT", 6))
+    if (!TokenMatch(mBuffer.data, "OBJECT", 6)) {
         return false;
+    }
 
     SkipSpaces(&mBuffer.data, mBuffer.end);
 
@@ -192,7 +189,6 @@ bool AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
         light->mAttenuationConstant = 1.f;
 
         // Generate a default name for both the light source and the node
-        // FIXME - what's the right way to print a size_t? Is 'zu' universally available? stick with the safe version.
         light->mName.length = ::ai_snprintf(light->mName.data, AI_MAXLEN, "ACLight_%i", static_cast<unsigned int>(mLights->size()) - 1);
         obj.name = std::string(light->mName.data);
 
@@ -202,8 +198,10 @@ bool AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
         obj.type = Object::Group;
     } else if (!ASSIMP_strincmp(mBuffer.data, "world", 5)) {
         obj.type = Object::World;
-    } else
+    } else {
         obj.type = Object::Poly;
+    }
+
     while (GetNextLine()) {
         if (TokenMatch(mBuffer.data, "kids", 4)) {
             SkipSpaces(&mBuffer.data, mBuffer.end);
@@ -344,6 +342,7 @@ bool AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
         }
     }
     ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: \'kids\' line was expected");
+
     return false;
 }
 

+ 2 - 2
code/AssetLib/AC/ACLoader.h

@@ -63,7 +63,7 @@ namespace Assimp {
 class AC3DImporter : public BaseImporter {
 public:
     AC3DImporter();
-    ~AC3DImporter() override;
+    ~AC3DImporter() override = default;
 
     // Represents an AC3D material
     struct Material {
@@ -103,7 +103,7 @@ public:
 
         unsigned int mat, flags;
 
-        typedef std::pair<unsigned int, aiVector2D> SurfaceEntry;
+        using SurfaceEntry = std::pair<unsigned int, aiVector2D>;
         std::vector<SurfaceEntry> entries;
 
         // Type is low nibble of flags

+ 1 - 1
code/AssetLib/AMF/AMFImporter.cpp

@@ -474,7 +474,7 @@ void AMFImporter::ParseNode_Metadata(XmlNode &node) {
 
     // read attribute
     ne = new AMFMetadata(mNodeElement_Cur);
-    ((AMFMetadata *)ne)->Type = type;
+    ((AMFMetadata *)ne)->MetaType = type;
     ((AMFMetadata *)ne)->Value = value;
     mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
     mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.

+ 7 - 6
code/AssetLib/AMF/AMFImporter_Node.hpp

@@ -86,7 +86,8 @@ public:
 	AMFNodeElementBase *Parent; ///< Parent element. If nullptr then this node is root.
 	std::list<AMFNodeElementBase *> Child; ///< Child elements.
 
-public: /// Destructor, virtual..
+public: 
+	/// Destructor, virtual..
 	virtual ~AMFNodeElementBase() = default;
 
 	/// Disabled copy constructor and co.
@@ -97,10 +98,10 @@ public: /// Destructor, virtual..
 
 protected:
 	/// In constructor inheritor must set element type.
-	/// \param [in] pType - element type.
+	/// \param [in] type - element type.
 	/// \param [in] pParent - parent element.
-	AMFNodeElementBase(const EType pType, AMFNodeElementBase *pParent) :
-			Type(pType), Parent(pParent) {
+	AMFNodeElementBase(EType type, AMFNodeElementBase *pParent) :
+			Type(type), Parent(pParent) {
 		// empty
 	}
 }; // class IAMFImporter_NodeElement
@@ -135,8 +136,8 @@ struct AMFInstance : public AMFNodeElementBase {
 /// Structure that define metadata node.
 struct AMFMetadata : public AMFNodeElementBase {
 
-	std::string Type; ///< Type of "Value".
-	std::string Value; ///< Value.
+	std::string MetaType; ///< Type of "Value".
+	std::string Value;    ///< Value.
 
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.

+ 1 - 1
code/AssetLib/AMF/AMFImporter_Postprocess.cpp

@@ -333,7 +333,7 @@ void AMFImporter::Postprocess_AddMetadata(const AMFMetaDataArray &metadataList,
     size_t meta_idx(0);
 
     for (const AMFMetadata *metadata : metadataList) {
-        sceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx++), metadata->Type, aiString(metadata->Value));
+        sceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx++), metadata->MetaType, aiString(metadata->Value));
     }
 }
 

+ 3 - 7
code/AssetLib/BVH/BVHLoader.cpp

@@ -6,8 +6,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2024, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -85,11 +83,9 @@ BVHLoader::BVHLoader() :
         mLine(),
         mAnimTickDuration(),
         mAnimNumFrames(),
-        noSkeletonMesh() {}
-
-// ------------------------------------------------------------------------------------------------
-// Destructor, private as well
-BVHLoader::~BVHLoader() = default;
+        noSkeletonMesh() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.

+ 7 - 12
code/AssetLib/BVH/BVHLoader.h

@@ -80,32 +80,27 @@ class BVHLoader : public BaseImporter {
         std::vector<ChannelType> mChannels;
         std::vector<float> mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames
 
-        Node() :
-                mNode(nullptr) {}
-
-        explicit Node(const aiNode *pNode) :
-                mNode(pNode) {}
+        Node() : mNode(nullptr) {}
+        explicit Node(const aiNode *pNode) :mNode(pNode) {}
     };
 
 public:
     BVHLoader();
-    ~BVHLoader();
+    ~BVHLoader() override = default;
 
-public:
     /** Returns whether the class can handle the format of the given file.
      * See BaseImporter::CanRead() for details. */
-    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const;
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const override;
 
-    void SetupProperties(const Importer *pImp);
-    const aiImporterDesc *GetInfo() const;
+    void SetupProperties(const Importer *pImp) override;
+    const aiImporterDesc *GetInfo() const override;
 
 protected:
     /** Imports the given file into the given scene structure.
      * See BaseImporter::InternReadFile() for details
      */
-    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
+    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
 
-protected:
     /** Reads the file */
     void ReadStructure(aiScene *pScene);
 

+ 0 - 1
code/AssetLib/Blender/BlenderDNA.h

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

+ 3 - 1
code/AssetLib/Blender/BlenderDNA.inl

@@ -841,5 +841,7 @@ template <template <typename> class TOUT> template <typename T> void ObjectCache
 #endif
 }
 
-}}
+}
+}
+
 #endif

+ 0 - 1
code/AssetLib/Blender/BlenderIntermediate.h

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

+ 2 - 10
code/AssetLib/C4D/C4DImporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2024, assimp team
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -86,7 +86,7 @@ namespace Assimp {
     }
 }
 
-static const aiImporterDesc desc = {
+static constexpr aiImporterDesc desc = {
     "Cinema4D Importer",
     "",
     "",
@@ -99,13 +99,6 @@ static const aiImporterDesc desc = {
     "c4d"
 };
 
-
-// ------------------------------------------------------------------------------------------------
-C4DImporter::C4DImporter() = default;
-
-// ------------------------------------------------------------------------------------------------
-C4DImporter::~C4DImporter() = default;
-
 // ------------------------------------------------------------------------------------------------
 bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const {
     const std::string& extension = GetExtension(pFile);
@@ -196,7 +189,6 @@ void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
     std::copy(materials.begin(), materials.end(), pScene->mMaterials);
 }
 
-
 // ------------------------------------------------------------------------------------------------
 bool C4DImporter::ReadShader(aiMaterial* out, BaseShader* shader) {
     // based on Cineware sample code (C4DImportExport.cpp)

+ 2 - 2
code/AssetLib/C4D/C4DImporter.h

@@ -78,8 +78,8 @@ namespace Assimp  {
 // -------------------------------------------------------------------------------------------
 class C4DImporter : public BaseImporter, public LogFunctions<C4DImporter> {
 public:
-    C4DImporter();
-    ~C4DImporter() override;
+    C4DImporter() = default;
+    ~C4DImporter() override = default;
     bool CanRead( const std::string& pFile, IOSystem*, bool checkSig) const override;
 
 protected:

+ 1 - 1
code/AssetLib/COB/COBLoader.cpp

@@ -228,7 +228,7 @@ aiNode *COBImporter::BuildNodes(const Node &root, const Scene &scin, aiScene *fi
         const Mesh &ndmesh = (const Mesh &)(root);
         if (ndmesh.vertex_positions.size() && ndmesh.texture_coords.size()) {
 
-            typedef std::pair<const unsigned int, Mesh::FaceRefList> Entry;
+            using Entry = std::pair<const unsigned int, Mesh::FaceRefList>;
             for (const Entry &reflist : ndmesh.temp_map) {
                 { // create mesh
                     size_t n = 0;

+ 4 - 8
code/AssetLib/CSM/CSMLoader.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2024, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -73,10 +71,9 @@ static constexpr aiImporterDesc desc = {
     "csm"
 };
 
-
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
-CSMImporter::CSMImporter() : noSkeletonMesh(){
+CSMImporter::CSMImporter() : noSkeletonMesh() {
     // empty
 }
 
@@ -102,8 +99,7 @@ void CSMImporter::SetupProperties(const Importer* pImp) {
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
 void CSMImporter::InternReadFile( const std::string& pFile,
-    aiScene* pScene, IOSystem* pIOHandler)
-{
+        aiScene* pScene, IOSystem* pIOHandler) {
     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
 
     // Check whether we can read from the file
@@ -127,11 +123,11 @@ void CSMImporter::InternReadFile( const std::string& pFile,
 
         if ('$'  == *buffer)    {
             ++buffer;
-            if (TokenMatchI(buffer,"firstframe",10))    {
+            if (TokenMatchI(buffer,"firstframe",10)) {
                 SkipSpaces(&buffer, end);
                 first = strtol10(buffer,&buffer);
             }
-            else if (TokenMatchI(buffer,"lastframe",9))     {
+            else if (TokenMatchI(buffer,"lastframe",9)) {
                 SkipSpaces(&buffer, end);
                 last = strtol10(buffer,&buffer);
             }

+ 310 - 252
code/AssetLib/Collada/ColladaExporter.cpp

@@ -62,6 +62,37 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp {
 
+static const aiNode *findSkeletonRootNode(const aiScene *scene, const aiMesh *mesh) {
+    std::set<const aiNode *> topParentBoneNodes;
+    if (mesh && mesh->mNumBones > 0) {
+        for (unsigned int i = 0; i < mesh->mNumBones; ++i) {
+            aiBone *bone = mesh->mBones[i];
+
+            const aiNode *node = scene->mRootNode->findBoneNode(bone);
+            if (node) {
+                while (node->mParent && scene->findBone(node->mParent->mName) != nullptr) {
+                    node = node->mParent;
+                }
+                topParentBoneNodes.insert(node);
+            }
+        }
+    }
+
+    if (!topParentBoneNodes.empty()) {
+        const aiNode *parentBoneNode = *topParentBoneNodes.begin();
+        if (topParentBoneNodes.size() == 1) {
+            return parentBoneNode;
+        } else {
+            for (auto it : topParentBoneNodes) {
+                if (it->mParent) return it->mParent;
+            }
+            return parentBoneNode;
+        }
+    }
+
+    return nullptr;
+}
+
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
 void ExportSceneCollada(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
@@ -325,60 +356,68 @@ void ColladaExporter::WriteHeader() {
 // ------------------------------------------------------------------------------------------------
 // Write the embedded textures
 void ColladaExporter::WriteTextures() {
-    static const unsigned int buffer_size = 1024;
-    char str[buffer_size];
+    static constexpr unsigned int buffer_size = 1024;
+    char str[buffer_size] = {'\0'};
 
-    if (mScene->HasTextures()) {
-        for (unsigned int i = 0; i < mScene->mNumTextures; i++) {
-            // It would be great to be able to create a directory in portable standard C++, but it's not the case,
-            // so we just write the textures in the current directory.
-
-            aiTexture *texture = mScene->mTextures[i];
-            if (nullptr == texture) {
-                continue;
-            }
+    if (!mScene->HasTextures()) {
+        return;
+    }
 
-            ASSIMP_itoa10(str, buffer_size, i + 1);
+    for (unsigned int i = 0; i < mScene->mNumTextures; i++) {
+        // It would be great to be able to create a directory in portable standard C++, but it's not the case,
+        // so we just write the textures in the current directory.
 
-            std::string name = mFile + "_texture_" + (i < 1000 ? "0" : "") + (i < 100 ? "0" : "") + (i < 10 ? "0" : "") + str + "." + ((const char *)texture->achFormatHint);
+        aiTexture *texture = mScene->mTextures[i];
+        if (nullptr == texture) {
+            continue;
+        }
 
-            std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath + mIOSystem->getOsSeparator() + name, "wb"));
-            if (outfile == nullptr) {
-                throw DeadlyExportError("could not open output texture file: " + mPath + name);
-            }
+        ASSIMP_itoa10(str, buffer_size, i + 1);
 
-            if (texture->mHeight == 0) {
-                outfile->Write((void *)texture->pcData, texture->mWidth, 1);
-            } else {
-                Bitmap::Save(texture, outfile.get());
-            }
+        std::string name = mFile + "_texture_" + (i < 1000 ? "0" : "") + (i < 100 ? "0" : "") + (i < 10 ? "0" : "") + str + "." + ((const char *)texture->achFormatHint);
 
-            outfile->Flush();
+        std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath + mIOSystem->getOsSeparator() + name, "wb"));
+        if (outfile == nullptr) {
+            throw DeadlyExportError("could not open output texture file: " + mPath + name);
+        }
 
-            textures.insert(std::make_pair(i, name));
+        if (texture->mHeight == 0) {
+            outfile->Write((void *)texture->pcData, texture->mWidth, 1);
+        } else {
+            Bitmap::Save(texture, outfile.get());
         }
+
+        outfile->Flush();
+
+        textures.insert(std::make_pair(i, name));
     }
 }
 
 // ------------------------------------------------------------------------------------------------
 // Write the embedded textures
 void ColladaExporter::WriteCamerasLibrary() {
-    if (mScene->HasCameras()) {
-
-        mOutput << startstr << "<library_cameras>" << endstr;
-        PushTag();
+    if (!mScene->HasCameras()) {
+        return;
+    }
 
-        for (size_t a = 0; a < mScene->mNumCameras; ++a)
-            WriteCamera(a);
+    mOutput << startstr << "<library_cameras>" << endstr;
+    PushTag();
 
-        PopTag();
-        mOutput << startstr << "</library_cameras>" << endstr;
+    for (size_t a = 0; a < mScene->mNumCameras; ++a) {
+        WriteCamera(a);
     }
+
+    PopTag();
+    mOutput << startstr << "</library_cameras>" << endstr;
 }
 
 void ColladaExporter::WriteCamera(size_t pIndex) {
 
     const aiCamera *cam = mScene->mCameras[pIndex];
+    if (cam == nullptr) {
+        return;
+    }
+
     const std::string cameraId = GetObjectUniqueId(AiObjectType::Camera, pIndex);
     const std::string cameraName = GetObjectName(AiObjectType::Camera, pIndex);
 
@@ -416,22 +455,27 @@ void ColladaExporter::WriteCamera(size_t pIndex) {
 // ------------------------------------------------------------------------------------------------
 // Write the embedded textures
 void ColladaExporter::WriteLightsLibrary() {
-    if (mScene->HasLights()) {
-
-        mOutput << startstr << "<library_lights>" << endstr;
-        PushTag();
+    if (!mScene->HasLights()) {
+        return;
+    }
 
-        for (size_t a = 0; a < mScene->mNumLights; ++a)
-            WriteLight(a);
+    mOutput << startstr << "<library_lights>" << endstr;
+    PushTag();
 
-        PopTag();
-        mOutput << startstr << "</library_lights>" << endstr;
+    for (size_t a = 0; a < mScene->mNumLights; ++a) {
+        WriteLight(a);
     }
+
+    PopTag();
+    mOutput << startstr << "</library_lights>" << endstr;
 }
 
 void ColladaExporter::WriteLight(size_t pIndex) {
 
     const aiLight *light = mScene->mLights[pIndex];
+    if (light == nullptr) {
+        return;
+    }
     const std::string lightId = GetObjectUniqueId(AiObjectType::Light, pIndex);
     const std::string lightName = GetObjectName(AiObjectType::Light, pIndex);
 
@@ -456,6 +500,7 @@ void ColladaExporter::WriteLight(size_t pIndex) {
     case aiLightSource_AREA:
     case aiLightSource_UNDEFINED:
     case _aiLightSource_Force32Bit:
+    default:
         break;
     }
     PopTag();
@@ -515,10 +560,6 @@ void ColladaExporter::WriteSpotLight(const aiLight *const light) {
     mOutput << startstr << "<quadratic_attenuation>"
             << light->mAttenuationQuadratic
             << "</quadratic_attenuation>" << endstr;
-    /*
-    out->mAngleOuterCone = AI_DEG_TO_RAD (std::acos(std::pow(0.1f,1.f/srcLight->mFalloffExponent))+
-                            srcLight->mFalloffAngle);
-    */
 
     const ai_real fallOffAngle = AI_RAD_TO_DEG(light->mAngleInnerCone);
     mOutput << startstr << "<falloff_angle sid=\"fall_off_angle\">"
@@ -552,43 +593,44 @@ void ColladaExporter::WriteAmbientLight(const aiLight *const light) {
 
 // ------------------------------------------------------------------------------------------------
 // Reads a single surface entry from the given material keys
-bool ColladaExporter::ReadMaterialSurface(Surface &poSurface, const aiMaterial &pSrcMat, aiTextureType pTexture,
-        const char *pKey, size_t pType, size_t pIndex) {
-    if (pSrcMat.GetTextureCount(pTexture) > 0) {
-        aiString texfile;
-        unsigned int uvChannel = 0;
-        pSrcMat.GetTexture(pTexture, 0, &texfile, nullptr, &uvChannel);
+bool ColladaExporter::ReadMaterialSurface(Surface &poSurface, const aiMaterial &pSrcMat, aiTextureType pTexture, const char *pKey, size_t pType, size_t pIndex) {
+    if (pSrcMat.GetTextureCount(pTexture) == 0) {
+        if (pKey)
+            poSurface.exist = pSrcMat.Get(pKey, static_cast<unsigned int>(pType), static_cast<unsigned int>(pIndex), poSurface.color) == aiReturn_SUCCESS;
+        return poSurface.exist;
+    }
 
-        std::string index_str(texfile.C_Str());
+    aiString texfile;
+    unsigned int uvChannel = 0;
+    pSrcMat.GetTexture(pTexture, 0, &texfile, nullptr, &uvChannel);
 
-        if (index_str.size() != 0 && index_str[0] == '*') {
-            unsigned int index;
+    std::string index_str(texfile.C_Str());
 
-            index_str = index_str.substr(1, std::string::npos);
+    if (index_str.size() != 0 && index_str[0] == '*') {
+        unsigned int index;
 
-            try {
-                index = (unsigned int)strtoul10_64<DeadlyExportError>(index_str.c_str());
-            } catch (std::exception &error) {
-                throw DeadlyExportError(error.what());
-            }
+        index_str = index_str.substr(1, std::string::npos);
 
-            std::map<unsigned int, std::string>::const_iterator name = textures.find(index);
+        try {
+            index = (unsigned int)strtoul10_64<DeadlyExportError>(index_str.c_str());
+        } catch (std::exception &error) {
+            throw DeadlyExportError(error.what());
+        }
 
-            if (name != textures.end()) {
-                poSurface.texture = name->second;
-            } else {
-                throw DeadlyExportError("could not find embedded texture at index " + index_str);
-            }
+        std::map<unsigned int, std::string>::const_iterator name = textures.find(index);
+
+        if (name != textures.end()) {
+            poSurface.texture = name->second;
         } else {
-            poSurface.texture = texfile.C_Str();
+            throw DeadlyExportError("could not find embedded texture at index " + index_str);
         }
-
-        poSurface.channel = uvChannel;
-        poSurface.exist = true;
     } else {
-        if (pKey)
-            poSurface.exist = pSrcMat.Get(pKey, static_cast<unsigned int>(pType), static_cast<unsigned int>(pIndex), poSurface.color) == aiReturn_SUCCESS;
+        poSurface.texture = texfile.C_Str();
     }
+
+    poSurface.channel = uvChannel;
+    poSurface.exist = true;
+
     return poSurface.exist;
 }
 
@@ -601,79 +643,87 @@ static bool isalnum_C(char c) {
 // ------------------------------------------------------------------------------------------------
 // Writes an image entry for the given surface
 void ColladaExporter::WriteImageEntry(const Surface &pSurface, const std::string &imageId) {
-    if (!pSurface.texture.empty()) {
-        mOutput << startstr << "<image id=\"" << imageId << "\">" << endstr;
-        PushTag();
-        mOutput << startstr << "<init_from>";
+    if (pSurface.texture.empty()) {
+        return;
+    }
 
-        // URL encode image file name first, then XML encode on top
-        std::stringstream imageUrlEncoded;
-        for (std::string::const_iterator it = pSurface.texture.begin(); it != pSurface.texture.end(); ++it) {
-            if (isalnum_C((unsigned char)*it) || *it == ':' || *it == '_' || *it == '-' || *it == '.' || *it == '/' || *it == '\\')
-                imageUrlEncoded << *it;
-            else
-                imageUrlEncoded << '%' << std::hex << size_t((unsigned char)*it) << std::dec;
-        }
-        mOutput << XMLEscape(imageUrlEncoded.str());
-        mOutput << "</init_from>" << endstr;
-        PopTag();
-        mOutput << startstr << "</image>" << endstr;
+    mOutput << startstr << "<image id=\"" << imageId << "\">" << endstr;
+    PushTag();
+    mOutput << startstr << "<init_from>";
+
+    // URL encode image file name first, then XML encode on top
+    std::stringstream imageUrlEncoded;
+    for (std::string::const_iterator it = pSurface.texture.begin(); it != pSurface.texture.end(); ++it) {
+        if (isalnum_C((unsigned char)*it) || *it == ':' || *it == '_' || *it == '-' || *it == '.' || *it == '/' || *it == '\\')
+            imageUrlEncoded << *it;
+        else
+            imageUrlEncoded << '%' << std::hex << size_t((unsigned char)*it) << std::dec;
     }
+    mOutput << XMLEscape(imageUrlEncoded.str());
+    mOutput << "</init_from>" << endstr;
+    PopTag();
+    mOutput << startstr << "</image>" << endstr;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Writes a color-or-texture entry into an effect definition
 void ColladaExporter::WriteTextureColorEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &imageId) {
-    if (pSurface.exist) {
-        mOutput << startstr << "<" << pTypeName << ">" << endstr;
-        PushTag();
-        if (pSurface.texture.empty()) {
-            mOutput << startstr << "<color sid=\"" << pTypeName << "\">" << pSurface.color.r << "   " << pSurface.color.g << "   " << pSurface.color.b << "   " << pSurface.color.a << "</color>" << endstr;
-        } else {
-            mOutput << startstr << "<texture texture=\"" << imageId << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
-        }
-        PopTag();
-        mOutput << startstr << "</" << pTypeName << ">" << endstr;
+    if (!pSurface.exist) {
+        return;
+    }
+
+    mOutput << startstr << "<" << pTypeName << ">" << endstr;
+    PushTag();
+    if (pSurface.texture.empty()) {
+        mOutput << startstr << "<color sid=\"" << pTypeName << "\">" << pSurface.color.r << "   " << pSurface.color.g << "   " << pSurface.color.b << "   " << pSurface.color.a << "</color>" << endstr;
+    } else {
+        mOutput << startstr << "<texture texture=\"" << imageId << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
     }
+    PopTag();
+    mOutput << startstr << "</" << pTypeName << ">" << endstr;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Writes the two parameters necessary for referencing a texture in an effect entry
 void ColladaExporter::WriteTextureParamEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &materialId) {
     // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture
-    if (!pSurface.texture.empty()) {
-        mOutput << startstr << "<newparam sid=\"" << materialId << "-" << pTypeName << "-surface\">" << endstr;
-        PushTag();
-        mOutput << startstr << "<surface type=\"2D\">" << endstr;
-        PushTag();
-        mOutput << startstr << "<init_from>" << materialId << "-" << pTypeName << "-image</init_from>" << endstr;
-        PopTag();
-        mOutput << startstr << "</surface>" << endstr;
-        PopTag();
-        mOutput << startstr << "</newparam>" << endstr;
-
-        mOutput << startstr << "<newparam sid=\"" << materialId << "-" << pTypeName << "-sampler\">" << endstr;
-        PushTag();
-        mOutput << startstr << "<sampler2D>" << endstr;
-        PushTag();
-        mOutput << startstr << "<source>" << materialId << "-" << pTypeName << "-surface</source>" << endstr;
-        PopTag();
-        mOutput << startstr << "</sampler2D>" << endstr;
-        PopTag();
-        mOutput << startstr << "</newparam>" << endstr;
+    if (pSurface.texture.empty()) {
+        return;
     }
+
+    mOutput << startstr << "<newparam sid=\"" << materialId << "-" << pTypeName << "-surface\">" << endstr;
+    PushTag();
+    mOutput << startstr << "<surface type=\"2D\">" << endstr;
+    PushTag();
+    mOutput << startstr << "<init_from>" << materialId << "-" << pTypeName << "-image</init_from>" << endstr;
+    PopTag();
+    mOutput << startstr << "</surface>" << endstr;
+    PopTag();
+    mOutput << startstr << "</newparam>" << endstr;
+
+    mOutput << startstr << "<newparam sid=\"" << materialId << "-" << pTypeName << "-sampler\">" << endstr;
+    PushTag();
+    mOutput << startstr << "<sampler2D>" << endstr;
+    PushTag();
+    mOutput << startstr << "<source>" << materialId << "-" << pTypeName << "-surface</source>" << endstr;
+    PopTag();
+    mOutput << startstr << "</sampler2D>" << endstr;
+    PopTag();
+    mOutput << startstr << "</newparam>" << endstr;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Writes a scalar property
 void ColladaExporter::WriteFloatEntry(const Property &pProperty, const std::string &pTypeName) {
-    if (pProperty.exist) {
-        mOutput << startstr << "<" << pTypeName << ">" << endstr;
-        PushTag();
-        mOutput << startstr << "<float sid=\"" << pTypeName << "\">" << pProperty.value << "</float>" << endstr;
-        PopTag();
-        mOutput << startstr << "</" << pTypeName << ">" << endstr;
+    if (!pProperty.exist) {
+        return;
     }
+
+    mOutput << startstr << "<" << pTypeName << ">" << endstr;
+    PushTag();
+    mOutput << startstr << "<float sid=\"" << pTypeName << "\">" << pProperty.value << "</float>" << endstr;
+    PopTag();
+    mOutput << startstr << "</" << pTypeName << ">" << endstr;
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -827,8 +877,9 @@ void ColladaExporter::WriteControllerLibrary() {
 void ColladaExporter::WriteController(size_t pIndex) {
     const aiMesh *mesh = mScene->mMeshes[pIndex];
     // Is there a skin controller?
-    if (mesh->mNumBones == 0 || mesh->mNumFaces == 0 || mesh->mNumVertices == 0)
+    if (mesh->mNumBones == 0 || mesh->mNumFaces == 0 || mesh->mNumVertices == 0) {
         return;
+    }
 
     const std::string idstr = GetObjectUniqueId(AiObjectType::Mesh, pIndex);
     const std::string namestr = GetObjectName(AiObjectType::Mesh, pIndex);
@@ -859,8 +910,9 @@ void ColladaExporter::WriteController(size_t pIndex) {
 
     mOutput << startstr << "<Name_array id=\"" << idstr << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">";
 
-    for (size_t i = 0; i < mesh->mNumBones; ++i)
+    for (size_t i = 0; i < mesh->mNumBones; ++i) {
         mOutput << GetBoneUniqueId(mesh->mBones[i]) << ' ';
+    }
 
     mOutput << "</Name_array>" << endstr;
 
@@ -883,9 +935,11 @@ void ColladaExporter::WriteController(size_t pIndex) {
 
     std::vector<ai_real> bind_poses;
     bind_poses.reserve(mesh->mNumBones * 16);
-    for (unsigned int i = 0; i < mesh->mNumBones; ++i)
-        for (unsigned int j = 0; j < 4; ++j)
+    for (unsigned int i = 0; i < mesh->mNumBones; ++i) {
+        for (unsigned int j = 0; j < 4; ++j) {
             bind_poses.insert(bind_poses.end(), mesh->mBones[i]->mOffsetMatrix[j], mesh->mBones[i]->mOffsetMatrix[j] + 4);
+        }
+    }
 
     WriteFloatArray(idstr + "-skin-bind_poses", FloatType_Mat4x4, (const ai_real *)bind_poses.data(), bind_poses.size() / 16);
 
@@ -893,9 +947,11 @@ void ColladaExporter::WriteController(size_t pIndex) {
 
     std::vector<ai_real> skin_weights;
     skin_weights.reserve(mesh->mNumVertices * mesh->mNumBones);
-    for (size_t i = 0; i < mesh->mNumBones; ++i)
-        for (size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j)
+    for (size_t i = 0; i < mesh->mNumBones; ++i) {
+        for (size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
             skin_weights.push_back(mesh->mBones[i]->mWeights[j].mWeight);
+        }
+    }
 
     WriteFloatArray(idstr + "-skin-weights", FloatType_Weight, (const ai_real *)skin_weights.data(), skin_weights.size());
 
@@ -919,12 +975,15 @@ void ColladaExporter::WriteController(size_t pIndex) {
     mOutput << startstr << "<vcount>";
 
     std::vector<ai_uint> num_influences(mesh->mNumVertices, (ai_uint)0);
-    for (size_t i = 0; i < mesh->mNumBones; ++i)
-        for (size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j)
+    for (size_t i = 0; i < mesh->mNumBones; ++i) {
+        for (size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
             ++num_influences[mesh->mBones[i]->mWeights[j].mVertexId];
+        }
+    }
 
-    for (size_t i = 0; i < mesh->mNumVertices; ++i)
+    for (size_t i = 0; i < mesh->mNumVertices; ++i) {
         mOutput << num_influences[i] << " ";
+    }
 
     mOutput << "</vcount>" << endstr;
 
@@ -940,7 +999,7 @@ void ColladaExporter::WriteController(size_t pIndex) {
 
     ai_uint weight_index = 0;
     std::vector<ai_int> joint_weight_indices(2 * joint_weight_indices_length, (ai_int)-1);
-    for (unsigned int i = 0; i < mesh->mNumBones; ++i)
+    for (unsigned int i = 0; i < mesh->mNumBones; ++i) {
         for (unsigned j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
             unsigned int vId = mesh->mBones[i]->mWeights[j].mVertexId;
             for (ai_uint k = 0; k < num_influences[vId]; ++k) {
@@ -952,9 +1011,11 @@ void ColladaExporter::WriteController(size_t pIndex) {
             }
             ++weight_index;
         }
+    }
 
-    for (size_t i = 0; i < joint_weight_indices.size(); ++i)
+    for (size_t i = 0; i < joint_weight_indices.size(); ++i) {
         mOutput << joint_weight_indices[i] << " ";
+    }
 
     num_influences.clear();
     accum_influences.clear();
@@ -978,8 +1039,9 @@ void ColladaExporter::WriteGeometryLibrary() {
     mOutput << startstr << "<library_geometries>" << endstr;
     PushTag();
 
-    for (size_t a = 0; a < mScene->mNumMeshes; ++a)
+    for (size_t a = 0; a < mScene->mNumMeshes; ++a) {
         WriteGeometry(a);
+    }
 
     PopTag();
     mOutput << startstr << "</library_geometries>" << endstr;
@@ -992,8 +1054,9 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
     const std::string geometryId = GetObjectUniqueId(AiObjectType::Mesh, pIndex);
     const std::string geometryName = GetObjectName(AiObjectType::Mesh, pIndex);
 
-    if (mesh->mNumFaces == 0 || mesh->mNumVertices == 0)
+    if (mesh->mNumFaces == 0 || mesh->mNumVertices == 0) {
         return;
+    }
 
     // opening tag
     mOutput << startstr << "<geometry id=\"" << geometryId << "\" name=\"" << geometryName << "\" >" << endstr;
@@ -1005,8 +1068,9 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
     // Positions
     WriteFloatArray(geometryId + "-positions", FloatType_Vector, (ai_real *)mesh->mVertices, mesh->mNumVertices);
     // Normals, if any
-    if (mesh->HasNormals())
+    if (mesh->HasNormals()) {
         WriteFloatArray(geometryId + "-normals", FloatType_Vector, (ai_real *)mesh->mNormals, mesh->mNumVertices);
+    }
 
     // texture coords
     for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
@@ -1035,10 +1099,11 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
     int countLines = 0;
     int countPoly = 0;
     for (size_t a = 0; a < mesh->mNumFaces; ++a) {
-        if (mesh->mFaces[a].mNumIndices == 2)
+        if (mesh->mFaces[a].mNumIndices == 2) {
             countLines++;
-        else if (mesh->mFaces[a].mNumIndices >= 3)
+        } else if (mesh->mFaces[a].mNumIndices >= 3) {
             countPoly++;
+        }
     }
 
     // lines
@@ -1046,13 +1111,18 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
         mOutput << startstr << "<lines count=\"" << countLines << "\" material=\"defaultMaterial\">" << endstr;
         PushTag();
         mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
-        if (mesh->HasNormals())
+        if (mesh->HasNormals()) {
             mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
+        }
         for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
-            if (mesh->HasTextureCoords(static_cast<unsigned int>(a)))
-                mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" "
+            if (mesh->HasTextureCoords(static_cast<unsigned int>(a))) {
+                mOutput << startstr 
+                        << "<input semantic=\"TEXCOORD\" source=\"#" 
+                        << geometryId 
+                        << "-tex" << a << "\" "
                         << "set=\"" << a << "\""
                         << " />" << endstr;
+            }
         }
         for (size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) {
             if (mesh->HasVertexColors(static_cast<unsigned int>(a)))
@@ -1065,8 +1135,9 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
         for (size_t a = 0; a < mesh->mNumFaces; ++a) {
             const aiFace &face = mesh->mFaces[a];
             if (face.mNumIndices != 2) continue;
-            for (size_t b = 0; b < face.mNumIndices; ++b)
+            for (size_t b = 0; b < face.mNumIndices; ++b) {
                 mOutput << face.mIndices[b] << " ";
+            }
         }
         mOutput << "</p>" << endstr;
         PopTag();
@@ -1080,8 +1151,9 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
         mOutput << startstr << "<polylist count=\"" << countPoly << "\" material=\"defaultMaterial\">" << endstr;
         PushTag();
         mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
-        if (mesh->HasNormals())
+        if (mesh->HasNormals()) {
             mOutput << startstr << "<input offset=\"0\" semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
+        }
         for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
             if (mesh->HasTextureCoords(static_cast<unsigned int>(a)))
                 mOutput << startstr << "<input offset=\"0\" semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" "
@@ -1106,8 +1178,9 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
         for (size_t a = 0; a < mesh->mNumFaces; ++a) {
             const aiFace &face = mesh->mFaces[a];
             if (face.mNumIndices < 3) continue;
-            for (size_t b = 0; b < face.mNumIndices; ++b)
+            for (size_t b = 0; b < face.mNumIndices; ++b) {
                 mOutput << face.mIndices[b] << " ";
+            }
         }
         mOutput << "</p>" << endstr;
         PopTag();
@@ -1126,13 +1199,27 @@ void ColladaExporter::WriteGeometry(size_t pIndex) {
 void ColladaExporter::WriteFloatArray(const std::string &pIdString, FloatDataType pType, const ai_real *pData, size_t pElementCount) {
     size_t floatsPerElement = 0;
     switch (pType) {
-    case FloatType_Vector: floatsPerElement = 3; break;
-    case FloatType_TexCoord2: floatsPerElement = 2; break;
-    case FloatType_TexCoord3: floatsPerElement = 3; break;
-    case FloatType_Color: floatsPerElement = 3; break;
-    case FloatType_Mat4x4: floatsPerElement = 16; break;
-    case FloatType_Weight: floatsPerElement = 1; break;
-    case FloatType_Time: floatsPerElement = 1; break;
+    case FloatType_Vector: 
+        floatsPerElement = 3; 
+        break;
+    case FloatType_TexCoord2: 
+        floatsPerElement = 2; 
+        break;
+    case FloatType_TexCoord3: 
+        floatsPerElement = 3; 
+        break;
+    case FloatType_Color: 
+        floatsPerElement = 3; 
+        break;
+    case FloatType_Mat4x4: 
+        floatsPerElement = 16; 
+        break;
+    case FloatType_Weight: 
+        floatsPerElement = 1; 
+        break;
+    case FloatType_Time: 
+        floatsPerElement = 1; 
+        break;
     default:
         return;
     }
@@ -1158,8 +1245,9 @@ void ColladaExporter::WriteFloatArray(const std::string &pIdString, FloatDataTyp
             mOutput << pData[a * 4 + 2] << " ";
         }
     } else {
-        for (size_t a = 0; a < pElementCount * floatsPerElement; ++a)
+        for (size_t a = 0; a < pElementCount * floatsPerElement; ++a) {
             mOutput << pData[a] << " ";
+        }
     }
     mOutput << "</float_array>" << endstr;
     PopTag();
@@ -1251,9 +1339,13 @@ void ColladaExporter::WriteSceneLibrary() {
 // ------------------------------------------------------------------------------------------------
 void ColladaExporter::WriteAnimationLibrary(size_t pIndex) {
     const aiAnimation *anim = mScene->mAnimations[pIndex];
-
-    if (anim->mNumChannels == 0 && anim->mNumMeshChannels == 0 && anim->mNumMorphMeshChannels == 0)
+    if (anim == nullptr) {
+        return;
+    }
+    
+    if (anim->mNumChannels == 0 && anim->mNumMeshChannels == 0 && anim->mNumMorphMeshChannels == 0) {
         return;
+    }
 
     const std::string animationNameEscaped = GetObjectName(AiObjectType::Animation, pIndex);
     const std::string idstrEscaped = GetObjectUniqueId(AiObjectType::Animation, pIndex);
@@ -1264,8 +1356,11 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) {
     std::string cur_node_idstr;
     for (size_t a = 0; a < anim->mNumChannels; ++a) {
         const aiNodeAnim *nodeAnim = anim->mChannels[a];
+        if (nodeAnim == nullptr) {
+            continue;
+        }
 
-        // sanity check
+        // sanity checks
         if (nodeAnim->mNumPositionKeys != nodeAnim->mNumScalingKeys || nodeAnim->mNumPositionKeys != nodeAnim->mNumRotationKeys) {
             continue;
         }
@@ -1364,6 +1459,9 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) {
 
     for (size_t a = 0; a < anim->mNumChannels; ++a) {
         const aiNodeAnim *nodeAnim = anim->mChannels[a];
+        if (nodeAnim == nullptr) {
+            continue;
+        }
 
         {
             // samplers
@@ -1382,97 +1480,42 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) {
 
     for (size_t a = 0; a < anim->mNumChannels; ++a) {
         const aiNodeAnim *nodeAnim = anim->mChannels[a];
+        if (nodeAnim == nullptr) {
+            continue;
+        }
 
         {
             // channels
-            mOutput << startstr << "<channel source=\"#" << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-sampler")) << "\" target=\"" << XMLIDEncode(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr;
+            mOutput << startstr 
+                    << "<channel source=\"#" 
+                    << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-sampler")) 
+                    << "\" target=\"" 
+                    << XMLIDEncode(nodeAnim->mNodeName.data) 
+                    << "/matrix\"/>" 
+                    << endstr;
         }
     }
 
     PopTag();
     mOutput << startstr << "</animation>" << endstr;
 }
-// ------------------------------------------------------------------------------------------------
-void ColladaExporter::WriteAnimationsLibrary() {
-    if (mScene->mNumAnimations > 0) {
-        mOutput << startstr << "<library_animations>" << endstr;
-        PushTag();
-
-        // start recursive write at the root node
-        for (size_t a = 0; a < mScene->mNumAnimations; ++a)
-            WriteAnimationLibrary(a);
-
-        PopTag();
-        mOutput << startstr << "</library_animations>" << endstr;
-    }
-}
-// ------------------------------------------------------------------------------------------------
-// Helper to find a bone by name in the scene
-aiBone *findBone(const aiScene *scene, const aiString &name) {
-    for (size_t m = 0; m < scene->mNumMeshes; m++) {
-        aiMesh *mesh = scene->mMeshes[m];
-        for (size_t b = 0; b < mesh->mNumBones; b++) {
-            aiBone *bone = mesh->mBones[b];
-            if (name == bone->mName) {
-                return bone;
-            }
-        }
-    }
-    return nullptr;
-}
 
 // ------------------------------------------------------------------------------------------------
-// Helper to find the node associated with a bone in the scene
-const aiNode *findBoneNode(const aiNode *aNode, const aiBone *bone) {
-    if (aNode && bone && aNode->mName == bone->mName) {
-        return aNode;
-    }
-
-    if (aNode && bone) {
-        for (unsigned int i = 0; i < aNode->mNumChildren; ++i) {
-            aiNode *aChild = aNode->mChildren[i];
-            const aiNode *foundFromChild = nullptr;
-            if (aChild) {
-                foundFromChild = findBoneNode(aChild, bone);
-                if (foundFromChild) {
-                    return foundFromChild;
-                }
-            }
-        }
-    }
-
-    return nullptr;
-}
-
-const aiNode *findSkeletonRootNode(const aiScene *scene, const aiMesh *mesh) {
-    std::set<const aiNode *> topParentBoneNodes;
-    if (mesh && mesh->mNumBones > 0) {
-        for (unsigned int i = 0; i < mesh->mNumBones; ++i) {
-            aiBone *bone = mesh->mBones[i];
-
-            const aiNode *node = findBoneNode(scene->mRootNode, bone);
-            if (node) {
-                while (node->mParent && findBone(scene, node->mParent->mName) != nullptr) {
-                    node = node->mParent;
-                }
-                topParentBoneNodes.insert(node);
-            }
-        }
+void ColladaExporter::WriteAnimationsLibrary() {
+    if (mScene->mNumAnimations == 0) {
+        return;
     }
+    
+    mOutput << startstr << "<library_animations>" << endstr;
+    PushTag();
 
-    if (!topParentBoneNodes.empty()) {
-        const aiNode *parentBoneNode = *topParentBoneNodes.begin();
-        if (topParentBoneNodes.size() == 1) {
-            return parentBoneNode;
-        } else {
-            for (auto it : topParentBoneNodes) {
-                if (it->mParent) return it->mParent;
-            }
-            return parentBoneNode;
-        }
+    // start recursive write at the root node
+    for (size_t a = 0; a < mScene->mNumAnimations; ++a) {
+        WriteAnimationLibrary(a);
     }
 
-    return nullptr;
+    PopTag();
+    mOutput << startstr << "</library_animations>" << endstr;
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -1483,13 +1526,13 @@ void ColladaExporter::WriteNode(const aiNode *pNode) {
     // Assimp-specific: nodes with no name cannot be associated with bones
     const char *node_type;
     bool is_joint, is_skeleton_root = false;
-    if (pNode->mName.length == 0 || nullptr == findBone(mScene, pNode->mName)) {
+    if (pNode->mName.length == 0 || nullptr == mScene->findBone(pNode->mName)) {
         node_type = "NODE";
         is_joint = false;
     } else {
         node_type = "JOINT";
         is_joint = true;
-        if (!pNode->mParent || nullptr == findBone(mScene, pNode->mParent->mName)) {
+        if (!pNode->mParent || nullptr == mScene->findBone(pNode->mParent->mName)) {
             is_skeleton_root = true;
         }
     }
@@ -1527,7 +1570,6 @@ void ColladaExporter::WriteNode(const aiNode *pNode) {
     }
 
     // customized, sid should be 'matrix' to match with loader code.
-    //mOutput << startstr << "<matrix sid=\"transform\">";
     mOutput << startstr << "<matrix sid=\"matrix\">";
 
     mOutput << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << " ";
@@ -1551,7 +1593,6 @@ void ColladaExporter::WriteNode(const aiNode *pNode) {
                 break;
             }
         }
-
     } else
         // instance every geometry
         for (size_t a = 0; a < pNode->mNumMeshes; ++a) {
@@ -1607,8 +1648,9 @@ void ColladaExporter::WriteNode(const aiNode *pNode) {
         }
 
     // recurse into subnodes
-    for (size_t a = 0; a < pNode->mNumChildren; ++a)
+    for (size_t a = 0; a < pNode->mNumChildren; ++a) {
         WriteNode(pNode->mChildren[a]);
+    }
 
     PopTag();
     mOutput << startstr << "</node>" << endstr;
@@ -1623,8 +1665,9 @@ void ColladaExporter::CreateNodeIds(const aiNode *node) {
 std::string ColladaExporter::GetNodeUniqueId(const aiNode *node) {
     // Use the pointer as the key. This is safe because the scene is immutable.
     auto idIt = mNodeIdMap.find(node);
-    if (idIt != mNodeIdMap.cend())
+    if (idIt != mNodeIdMap.cend()) {
         return idIt->second;
+    }
 
     // Prefer the requested Collada Id if extant
     std::string idStr;
@@ -1635,36 +1678,42 @@ std::string ColladaExporter::GetNodeUniqueId(const aiNode *node) {
         idStr = node->mName.C_Str();
     }
     // Make sure the requested id is valid
-    if (idStr.empty())
+    if (idStr.empty()) {
         idStr = "node";
-    else
+    } else {
         idStr = XMLIDEncode(idStr);
+    }
 
     // Ensure it's unique
     idStr = MakeUniqueId(mUniqueIds, idStr, std::string());
     mUniqueIds.insert(idStr);
     mNodeIdMap.insert(std::make_pair(node, idStr));
+    
     return idStr;
 }
 
 std::string ColladaExporter::GetNodeName(const aiNode *node) {
-
+    if (node == nullptr) {
+        return std::string();
+    }
     return XMLEscape(node->mName.C_Str());
 }
 
 std::string ColladaExporter::GetBoneUniqueId(const aiBone *bone) {
     // Find the Node that is this Bone
-    const aiNode *boneNode = findBoneNode(mScene->mRootNode, bone);
-    if (boneNode == nullptr)
+    const aiNode *boneNode = mScene->mRootNode->findBoneNode(bone);
+    if (boneNode == nullptr) {
         return std::string();
+    }
 
     return GetNodeUniqueId(boneNode);
 }
 
 std::string ColladaExporter::GetObjectUniqueId(AiObjectType type, size_t pIndex) {
     auto idIt = GetObjectIdMap(type).find(pIndex);
-    if (idIt != GetObjectIdMap(type).cend())
+    if (idIt != GetObjectIdMap(type).cend()) {
         return idIt->second;
+    }
 
     // Not seen this object before, create and add
     NameIdPair result = AddObjectIndexToMaps(type, pIndex);
@@ -1673,8 +1722,9 @@ std::string ColladaExporter::GetObjectUniqueId(AiObjectType type, size_t pIndex)
 
 std::string ColladaExporter::GetObjectName(AiObjectType type, size_t pIndex) {
     auto objectName = GetObjectNameMap(type).find(pIndex);
-    if (objectName != GetObjectNameMap(type).cend())
+    if (objectName != GetObjectNameMap(type).cend()) {
         return objectName->second;
+    }
 
     // Not seen this object before, create and add
     NameIdPair result = AddObjectIndexToMaps(type, pIndex);
@@ -1694,9 +1744,15 @@ ColladaExporter::NameIdPair ColladaExporter::AddObjectIndexToMaps(AiObjectType t
 
     // Get the name and id postfix
     switch (type) {
-    case AiObjectType::Mesh: name = mScene->mMeshes[index]->mName.C_Str(); break;
-    case AiObjectType::Material: name = mScene->mMaterials[index]->GetName().C_Str(); break;
-    case AiObjectType::Animation: name = mScene->mAnimations[index]->mName.C_Str(); break;
+    case AiObjectType::Mesh: 
+        name = mScene->mMeshes[index]->mName.C_Str(); 
+        break;
+    case AiObjectType::Material: 
+        name = mScene->mMaterials[index]->GetName().C_Str(); 
+        break;
+    case AiObjectType::Animation: 
+        name = mScene->mAnimations[index]->mName.C_Str(); 
+        break;
     case AiObjectType::Light:
         name = mScene->mLights[index]->mName.C_Str();
         idPostfix = "-light";
@@ -1705,7 +1761,8 @@ ColladaExporter::NameIdPair ColladaExporter::AddObjectIndexToMaps(AiObjectType t
         name = mScene->mCameras[index]->mName.C_Str();
         idPostfix = "-camera";
         break;
-    case AiObjectType::Count: throw std::logic_error("ColladaExporter::AiObjectType::Count is not an object type");
+    case AiObjectType::Count: 
+        throw std::logic_error("ColladaExporter::AiObjectType::Count is not an object type");
     }
 
     if (name.empty()) {
@@ -1723,8 +1780,9 @@ ColladaExporter::NameIdPair ColladaExporter::AddObjectIndexToMaps(AiObjectType t
         idStr = XMLIDEncode(name);
     }
 
-    if (!name.empty())
+    if (!name.empty()) {
         name = XMLEscape(name);
+    }
 
     idStr = MakeUniqueId(mUniqueIds, idStr, idPostfix);
 
@@ -1738,5 +1796,5 @@ ColladaExporter::NameIdPair ColladaExporter::AddObjectIndexToMaps(AiObjectType t
 
 } // end of namespace Assimp
 
-#endif
-#endif
+#endif // ASSIMP_BUILD_NO_COLLADA_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

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

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

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

@@ -1077,6 +1077,7 @@ static float getWeightAtKey(const std::vector<MorphTimeValues> &values, int key,
             return mKey.mWeight;
         }
     }
+    
     // no value at key found, try to interpolate if present at other keys. if not, return zero
     // TODO: interpolation
     return 0.0f;

+ 21 - 18
code/AssetLib/Collada/ColladaLoader.h

@@ -6,7 +6,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2024, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -77,8 +76,11 @@ struct ColladaMeshIndex {
     }
 };
 
-/** Loader class to read Collada scenes. Collada is over-engineered to death, with every new iteration bringing
- * more useless stuff, so I limited the data to what I think is useful for games.
+/** 
+ * @brief Loader class to read Collada scenes. 
+ *
+ * Collada is over-engineered to death, with every new iteration bringing  more useless stuff, 
+ * so I limited the data to what I think is useful for games.
 */
 class ColladaLoader : public BaseImporter {
 public:
@@ -102,50 +104,51 @@ protected:
     /// See #BaseImporter::InternReadFile for the details
     void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
 
-    /** Recursively constructs a scene node for the given parser node and returns it. */
+    /// Recursively constructs a scene node for the given parser node and returns it.
     aiNode *BuildHierarchy(const ColladaParser &pParser, const Collada::Node *pNode);
 
-    /** Resolve node instances */
+    /// Resolve node instances
     void ResolveNodeInstances(const ColladaParser &pParser, const Collada::Node *pNode,
             std::vector<const Collada::Node *> &resolved);
 
-    /** Builds meshes for the given node and references them */
+    /// Builds meshes for the given node and references them 
     void BuildMeshesForNode(const ColladaParser &pParser, const Collada::Node *pNode,
             aiNode *pTarget);
 
+    /// Lookup for meshes by their name
     aiMesh *findMesh(const std::string &meshid);
 
-    /** Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh */
+    /// Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh
     aiMesh *CreateMesh(const ColladaParser &pParser, const Collada::Mesh *pSrcMesh, const Collada::SubMesh &pSubMesh,
             const Collada::Controller *pSrcController, size_t pStartVertex, size_t pStartFace);
 
-    /** Builds cameras for the given node and references them */
+    /// Builds cameras for the given node and references them
     void BuildCamerasForNode(const ColladaParser &pParser, const Collada::Node *pNode,
             aiNode *pTarget);
 
-    /** Builds lights for the given node and references them */
+    /// Builds lights for the given node and references them
     void BuildLightsForNode(const ColladaParser &pParser, const Collada::Node *pNode,
             aiNode *pTarget);
 
-    /** Stores all meshes in the given scene */
+    /// Stores all meshes in the given scene
     void StoreSceneMeshes(aiScene *pScene);
 
-    /** Stores all materials in the given scene */
+    /// Stores all materials in the given scene
     void StoreSceneMaterials(aiScene *pScene);
 
-    /** Stores all lights in the given scene */
+    /// Stores all lights in the given scene
     void StoreSceneLights(aiScene *pScene);
 
-    /** Stores all cameras in the given scene */
+    /// Stores all cameras in the given scene
     void StoreSceneCameras(aiScene *pScene);
 
-    /** Stores all textures in the given scene */
+    /// Stores all textures in the given scene
     void StoreSceneTextures(aiScene *pScene);
 
-    /** Stores all animations
-     * @param pScene target scene to store the anims
-     */
-    void StoreAnimations(aiScene *pScene, const ColladaParser &pParser);
+    /// Stores all animations
+    /// @param pScene   Target scene to store the anims
+    /// @param parser   The collada parser
+    void StoreAnimations(aiScene *pScene, const ColladaParser &parser);
 
     /** Stores all animations for the given source anim and its nested child animations
      * @param pScene target scene to store the anims

+ 8 - 19
code/AssetLib/DXF/DXFHelper.h

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2024, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -62,10 +61,7 @@ namespace DXF {
 // do NOT skip empty lines. In DXF files, they count as valid data.
 class LineReader {
 public:
-    LineReader(StreamReaderLE& reader)
-    : splitter(reader,false,true)
-    , groupcode( 0 )
-    , end() {
+    LineReader(StreamReaderLE& reader) : splitter(reader,false,true), groupcode( 0 ), end() {
         // empty
     }
 
@@ -165,8 +161,7 @@ private:
 
 // represents a POLYLINE or a LWPOLYLINE. or even a 3DFACE The data is converted as needed.
 struct PolyLine {
-    PolyLine()
-    : flags() {
+    PolyLine() : flags() {
         // empty
     }
 
@@ -182,10 +177,7 @@ struct PolyLine {
 
 // reference to a BLOCK. Specifies its own coordinate system.
 struct InsertBlock {
-    InsertBlock()
-    : pos()
-    , scale(1.f,1.f,1.f)
-    , angle() {
+    InsertBlock() : pos(0.f, 0.f, 0.f), scale(1.f,1.f,1.f), angle(0.0f) {
         // empty
     }
 
@@ -198,8 +190,7 @@ struct InsertBlock {
 
 
 // keeps track of all geometry in a single BLOCK.
-struct Block
-{
+struct Block {
     std::vector< std::shared_ptr<PolyLine> > lines;
     std::vector<InsertBlock> insertions;
 
@@ -207,14 +198,12 @@ struct Block
     aiVector3D base;
 };
 
-
-struct FileData
-{
+struct FileData {
     // note: the LAST block always contains the stuff from ENTITIES.
     std::vector<Block> blocks;
 };
 
-}
-} // Namespace Assimp
+} // namespace DXF
+} // namespace Assimp
 
-#endif
+#endif // INCLUDED_DXFHELPER_H

+ 22 - 20
code/AssetLib/FBX/FBXAnimation.cpp

@@ -143,31 +143,33 @@ AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element &element, cons
 
 // ------------------------------------------------------------------------------------------------
 const AnimationCurveMap &AnimationCurveNode::Curves() const {
-    if (curves.empty()) {
-        // resolve attached animation curves
-        const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurve");
-
-        for (const Connection *con : conns) {
+    if (!curves.empty()) {
+        return curves;
+    }
+        
+    // resolve attached animation curves
+    const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurve");
 
-            // link should go for a property
-            if (!con->PropertyName().length()) {
-                continue;
-            }
+    for (const Connection *con : conns) {
 
-            const Object *const ob = con->SourceObject();
-            if (nullptr == ob) {
-                DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring", &element);
-                continue;
-            }
+        // link should go for a property
+        if (!con->PropertyName().length()) {
+            continue;
+        }
 
-            const AnimationCurve *const anim = dynamic_cast<const AnimationCurve *>(ob);
-            if (nullptr == anim) {
-                DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve", &element);
-                continue;
-            }
+        const Object *const ob = con->SourceObject();
+        if (nullptr == ob) {
+            DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring", &element);
+            continue;
+        }
 
-            curves[con->PropertyName()] = anim;
+        const AnimationCurve *const anim = dynamic_cast<const AnimationCurve *>(ob);
+        if (nullptr == anim) {
+            DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve", &element);
+            continue;
         }
+
+        curves[con->PropertyName()] = anim;
     }
 
     return curves;

+ 9 - 52
code/AssetLib/FBX/FBXBinaryTokenizer.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2024, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -60,58 +59,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 namespace Assimp {
 namespace FBX {
 
-//enum Flag
-//{
-//   e_unknown_0 = 1 << 0,
-//   e_unknown_1 = 1 << 1,
-//   e_unknown_2 = 1 << 2,
-//   e_unknown_3 = 1 << 3,
-//   e_unknown_4 = 1 << 4,
-//   e_unknown_5 = 1 << 5,
-//   e_unknown_6 = 1 << 6,
-//   e_unknown_7 = 1 << 7,
-//   e_unknown_8 = 1 << 8,
-//   e_unknown_9 = 1 << 9,
-//   e_unknown_10 = 1 << 10,
-//   e_unknown_11 = 1 << 11,
-//   e_unknown_12 = 1 << 12,
-//   e_unknown_13 = 1 << 13,
-//   e_unknown_14 = 1 << 14,
-//   e_unknown_15 = 1 << 15,
-//   e_unknown_16 = 1 << 16,
-//   e_unknown_17 = 1 << 17,
-//   e_unknown_18 = 1 << 18,
-//   e_unknown_19 = 1 << 19,
-//   e_unknown_20 = 1 << 20,
-//   e_unknown_21 = 1 << 21,
-//   e_unknown_22 = 1 << 22,
-//   e_unknown_23 = 1 << 23,
-//   e_flag_field_size_64_bit = 1 << 24, // Not sure what is
-//   e_unknown_25 = 1 << 25,
-//   e_unknown_26 = 1 << 26,
-//   e_unknown_27 = 1 << 27,
-//   e_unknown_28 = 1 << 28,
-//   e_unknown_29 = 1 << 29,
-//   e_unknown_30 = 1 << 30,
-//   e_unknown_31 = 1 << 31
-//};
-//
-//bool check_flag(uint32_t flags, Flag to_check)
-//{
-//	return (flags & to_check) != 0;
-//}
 // ------------------------------------------------------------------------------------------------
-Token::Token(const char* sbegin, const char* send, TokenType type, size_t offset)
-    :
-    #ifdef DEBUG
-    contents(sbegin, static_cast<size_t>(send-sbegin)),
-    #endif
-    sbegin(sbegin)
-    , send(send)
-    , type(type)
-    , line(offset)
-    , column(BINARY_MARKER)
-{
+Token::Token(const char* sbegin, const char* send, TokenType type, size_t offset) :
+        #ifdef DEBUG
+        contents(sbegin, static_cast<size_t>(send-sbegin)),
+        #endif
+        sbegin(sbegin),
+        send(send),
+        type(type),
+        line(offset),
+        column(BINARY_MARKER) {
     ai_assert(sbegin);
     ai_assert(send);
 

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

@@ -51,7 +51,8 @@ namespace Assimp {
 namespace FBX {
 
 static constexpr size_t NumNullRecords = 25;
-const char NULL_RECORD[NumNullRecords] = { // 25 null bytes in 64-bit and 13 null bytes in 32-bit
+
+constexpr 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'
 }; // who knows why, it looks like two integers 32/64 bit (compressed and uncompressed sizes?) + 1 byte (might be compression type?)

+ 7 - 7
code/AssetLib/FBX/FBXConverter.cpp

@@ -438,7 +438,8 @@ void FBXConverter::ConvertLight(const Light &light, const std::string &orig_name
             out_light->mType = aiLightSource_UNDEFINED;
             break;
         default:
-            ai_assert(false);
+            FBXImporter::LogError("Not handled light type: ", light.LightType());
+            break;
     }
 
     float decay = light.DecayStart();
@@ -463,7 +464,7 @@ void FBXConverter::ConvertLight(const Light &light, const std::string &orig_name
             out_light->mAttenuationQuadratic = 1.0f;
             break;
         default:
-            ai_assert(false);
+            FBXImporter::LogError("Not handled light decay type: ", light.DecayType());
             break;
     }
 }
@@ -601,7 +602,7 @@ const char *FBXConverter::NameTransformationCompProperty(TransformationComp comp
             return "GeometricRotationInverse";
         case TransformationComp_GeometricTranslationInverse:
             return "GeometricTranslationInverse";
-        case TransformationComp_MAXIMUM: // this is to silence compiler warnings
+        case TransformationComp_MAXIMUM:
             break;
     }
 
@@ -3415,7 +3416,7 @@ FBXConverter::KeyFrameListList FBXConverter::GetRotationKeyframeList(const std::
     KeyFrameListList inputs;
     inputs.reserve(nodes.size() * 3);
 
-    //give some breathing room for rounding errors
+    // give some breathing room for rounding errors
     const int64_t adj_start = start - 10000;
     const int64_t adj_stop = stop + 10000;
 
@@ -3441,7 +3442,7 @@ FBXConverter::KeyFrameListList FBXConverter::GetRotationKeyframeList(const std::
             ai_assert(curve->GetKeys().size() == curve->GetValues().size());
             ai_assert(curve->GetKeys().size());
 
-            //get values within the start/stop time window
+            // get values within the start/stop time window
             std::shared_ptr<KeyTimeList> Keys(new KeyTimeList());
             std::shared_ptr<KeyValueList> Values(new KeyValueList());
             const size_t count = curve->GetKeys().size();
@@ -3461,8 +3462,7 @@ FBXConverter::KeyFrameListList FBXConverter::GetRotationKeyframeList(const std::
                         if (tnew >= adj_start && tnew <= adj_stop) {
                             Keys->push_back(tnew);
                             Values->push_back(vnew);
-                        }
-                        else {
+                        } else {
                             // Something broke
                             break;
                         }

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

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2024, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -70,7 +69,7 @@ struct morphKeyData {
     std::vector<unsigned int> values;
     std::vector<float> weights;
 };
-typedef std::map<int64_t, morphKeyData*> morphAnimData;
+using morphAnimData = std::map<int64_t, morphKeyData*> ;
 
 namespace Assimp {
 namespace FBX {

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

@@ -65,9 +65,6 @@ Deformer::Deformer(uint64_t id, const Element& element, const Document& doc, con
     props = GetPropertyTable(doc,"Deformer.Fbx" + classname,element,sc,true);
 }
 
-// ------------------------------------------------------------------------------------------------
-Deformer::~Deformer() = default;
-
 // ------------------------------------------------------------------------------------------------
 Cluster::Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name)
 : Deformer(id,element,doc,name)
@@ -113,10 +110,6 @@ Cluster::Cluster(uint64_t id, const Element& element, const Document& doc, const
     }
 }
 
-
-// ------------------------------------------------------------------------------------------------
-Cluster::~Cluster() = default;
-
 // ------------------------------------------------------------------------------------------------
 Skin::Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name)
 : Deformer(id,element,doc,name)
@@ -142,9 +135,6 @@ Skin::Skin(uint64_t id, const Element& element, const Document& doc, const std::
     }
 }
 
-
-// ------------------------------------------------------------------------------------------------
-Skin::~Skin() = default;
 // ------------------------------------------------------------------------------------------------
 BlendShape::BlendShape(uint64_t id, const Element& element, const Document& doc, const std::string& name)
     : Deformer(id, element, doc, name)
@@ -161,8 +151,7 @@ BlendShape::BlendShape(uint64_t id, const Element& element, const Document& doc,
         }
     }
 }
-// ------------------------------------------------------------------------------------------------
-BlendShape::~BlendShape() = default;
+
 // ------------------------------------------------------------------------------------------------
 BlendShapeChannel::BlendShapeChannel(uint64_t id, const Element& element, const Document& doc, const std::string& name)
     : Deformer(id, element, doc, name)
@@ -188,10 +177,8 @@ BlendShapeChannel::BlendShapeChannel(uint64_t id, const Element& element, const
         }
     }
 }
-// ------------------------------------------------------------------------------------------------
-BlendShapeChannel::~BlendShapeChannel() = default;
-// ------------------------------------------------------------------------------------------------
-}
-}
-#endif
 
+} // namespace FBX
+} // Namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_FBX_IMPORTER

+ 8 - 0
code/AssetLib/FBX/FBXDocument.cpp

@@ -663,6 +663,10 @@ LazyObject& Connection::LazyDestinationObject() const {
 const Object* Connection::SourceObject() const {
     LazyObject* const lazy = doc.GetObject(src);
     ai_assert(lazy);
+    if (lazy == nullptr) {
+        return nullptr;
+    }
+
     return lazy->Get();
 }
 
@@ -670,6 +674,10 @@ const Object* Connection::SourceObject() const {
 const Object* Connection::DestinationObject() const {
     LazyObject* const lazy = doc.GetObject(dest);
     ai_assert(lazy);
+    if (lazy == nullptr) {
+        return nullptr;
+    }
+
     return lazy->Get();
 }
 

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

@@ -657,11 +657,11 @@ private:
 };
 
 /** DOM class for generic FBX materials */
-class Material : public Object {
+class Material final : public Object {
 public:
     Material(uint64_t id, const Element& element, const Document& doc, const std::string& name);
 
-    virtual ~Material();
+    ~Material() override = default;
 
     const std::string& GetShadingModel() const {
         return shading;
@@ -835,7 +835,7 @@ private:
 class Deformer : public Object {
 public:
     Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name);
-    virtual ~Deformer();
+    virtual ~Deformer() = default;
 
     const PropertyTable& Props() const {
         ai_assert(props.get());
@@ -855,7 +855,7 @@ class BlendShapeChannel : public Deformer {
 public:
     BlendShapeChannel(uint64_t id, const Element& element, const Document& doc, const std::string& name);
 
-    virtual ~BlendShapeChannel();
+    virtual ~BlendShapeChannel() = default;
 
     float DeformPercent() const {
         return percent;
@@ -880,7 +880,7 @@ class BlendShape : public Deformer {
 public:
     BlendShape(uint64_t id, const Element& element, const Document& doc, const std::string& name);
 
-    virtual ~BlendShape();
+    virtual ~BlendShape() = default;
 
     const std::unordered_set<const BlendShapeChannel*>& BlendShapeChannels() const {
         return blendShapeChannels;
@@ -895,7 +895,7 @@ class Cluster : public Deformer {
 public:
     Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name);
 
-    virtual ~Cluster();
+    virtual ~Cluster() = default;
 
     /** get the list of deformer weights associated with this cluster.
      *  Use #GetIndices() to get the associated vertices. Both arrays
@@ -939,7 +939,7 @@ class Skin : public Deformer {
 public:
     Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name);
 
-    virtual ~Skin();
+    virtual ~Skin() = default;
 
     float DeformAccuracy() const {
         return accuracy;

+ 10 - 15
code/AssetLib/FBX/FBXDocumentUtil.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2024, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -36,7 +35,6 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 ----------------------------------------------------------------------
 */
 
@@ -81,8 +79,7 @@ void DOMWarning(const std::string& message, const Token& token) {
 }
 
 // ------------------------------------------------------------------------------------------------
-void DOMWarning(const std::string& message, const Element* element /*= nullptr*/)
-{
+void DOMWarning(const std::string& message, const Element* element /*= nullptr*/) {
     if(element) {
         DOMWarning(message,element->KeyToken());
         return;
@@ -92,41 +89,39 @@ void DOMWarning(const std::string& message, const Element* element /*= nullptr*/
     }
 }
 
-
 // ------------------------------------------------------------------------------------------------
 // fetch a property table and the corresponding property template
 std::shared_ptr<const PropertyTable> GetPropertyTable(const Document& doc,
-    const std::string& templateName,
-    const Element &element,
-    const Scope& sc,
-    bool no_warn /*= false*/)
-{
+        const std::string& templateName,
+        const Element &element,
+        const Scope& sc,
+        bool no_warn /*= false*/) {
     const Element* const Properties70 = sc["Properties70"];
     std::shared_ptr<const PropertyTable> templateProps = std::shared_ptr<const PropertyTable>(
             static_cast<const PropertyTable *>(nullptr));
 
-    if(templateName.length()) {
+    if (templateName.length()) {
         PropertyTemplateMap::const_iterator it = doc.Templates().find(templateName);
         if(it != doc.Templates().end()) {
             templateProps = (*it).second;
         }
     }
 
-    if(!Properties70 || !Properties70->Compound()) {
+    if (!Properties70 || !Properties70->Compound()) {
         if(!no_warn) {
             DOMWarning("property table (Properties70) not found",&element);
         }
         if(templateProps) {
             return templateProps;
-        }
-        else {
+        } else {
             return std::make_shared<const PropertyTable>();
         }
     }
     return std::make_shared<const PropertyTable>(*Properties70,templateProps);
 }
+
 } // !Util
 } // !FBX
 } // !Assimp
 
-#endif
+#endif // ASSIMP_BUILD_NO_FBX_IMPORTER

+ 1 - 1
code/AssetLib/FBX/FBXDocumentUtil.h

@@ -114,4 +114,4 @@ inline const T* ProcessSimpleConnection(const Connection& con,
 } //!FBX
 } //!Assimp
 
-#endif
+#endif // ASSIMP_BUILD_NO_FBX_IMPORTER

+ 13 - 20
code/AssetLib/FBX/FBXExportNode.cpp

@@ -35,7 +35,6 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 ----------------------------------------------------------------------
 */
 #ifndef ASSIMP_BUILD_NO_EXPORT
@@ -55,37 +54,31 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <memory> // shared_ptr
 
 namespace Assimp {
+
 // AddP70<type> helpers... there's no usable pattern here,
 // so all are defined as separate functions.
 // Even "animatable" properties are often completely different
 // from the standard (nonanimated) property definition,
 // so they are specified with an 'A' suffix.
 
-void FBX::Node::AddP70int(
-    const std::string& cur_name, int32_t value
-) {
+void FBX::Node::AddP70int(const std::string& cur_name, int32_t value) {
     FBX::Node n("P");
     n.AddProperties(cur_name, "int", "Integer", "", value);
     AddChild(n);
 }
 
-void FBX::Node::AddP70bool(
-    const std::string& cur_name, bool value
-) {
+void FBX::Node::AddP70bool(const std::string& cur_name, bool value) {
     FBX::Node n("P");
     n.AddProperties(cur_name, "bool", "", "", int32_t(value));
     AddChild(n);
 }
 
-void FBX::Node::AddP70double(
-        const std::string &cur_name, double value) {
-    FBX::Node n("P");
+void FBX::Node::AddP70double(const std::string &cur_name, double value) {    FBX::Node n("P");
     n.AddProperties(cur_name, "double", "Number", "", value);
     AddChild(n);
 }
 
-void FBX::Node::AddP70numberA(
-        const std::string &cur_name, double value) {
+void FBX::Node::AddP70numberA(const std::string &cur_name, double value) {
     FBX::Node n("P");
     n.AddProperties(cur_name, "Number", "", "A", value);
     AddChild(n);
@@ -405,8 +398,7 @@ void FBX::Node::DumpChildrenAscii(std::ostream& s, int indent)
     }
 }
 
-void FBX::Node::EndAscii(std::ostream& s, int indent, bool has_children)
-{
+void FBX::Node::EndAscii(std::ostream& s, int indent, bool has_children) {
     if (!has_children) { return; } // nothing to do
     s << '\n';
     for (int i = 0; i < indent; ++i) { s << '\t'; }
@@ -417,11 +409,10 @@ void FBX::Node::EndAscii(std::ostream& s, int indent, bool has_children)
 
 // ascii property node from vector of doubles
 void FBX::Node::WritePropertyNodeAscii(
-    const std::string& name,
-    const std::vector<double>& v,
-    Assimp::StreamWriterLE& s,
-    int indent
-){
+        const std::string& name,
+        const std::vector<double>& v,
+        Assimp::StreamWriterLE& s,
+        int indent){
     char buffer[32];
     FBX::Node node(name);
     node.Begin(s, false, indent);
@@ -556,6 +547,8 @@ void FBX::Node::WritePropertyNode(
         FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
     }
 }
-}
+
+} // namespace Assimp
+
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_EXPORT

+ 26 - 35
code/AssetLib/FBX/FBXExportNode.h

@@ -35,7 +35,6 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 ----------------------------------------------------------------------
 */
 
@@ -70,7 +69,6 @@ public:
     // some nodes always pretend they have children...
     bool force_has_children = false;
 
-public: // constructors
     /// The default class constructor.
     Node() = default;
 
@@ -89,7 +87,6 @@ public: // constructors
         AddProperties(std::forward<More>(more)...);
     }
 
-public: // functions to add properties or children
     // add a single property to the node
     template <typename T>
     void AddProperty(T&& value) {
@@ -118,8 +115,6 @@ public: // functions to add properties or children
         children.push_back(std::move(c));
     }
 
-public: // support specifically for dealing with Properties70 nodes
-
     // it really is simpler to make these all separate functions.
     // the versions with 'A' suffixes are for animatable properties.
     // those often follow a completely different format internally in FBX.
@@ -150,8 +145,6 @@ public: // support specifically for dealing with Properties70 nodes
         AddChild(n);
     }
 
-public: // member functions for writing data to a file or stream
-
     // write the full node to the given file or stream
     void Dump(
             const std::shared_ptr<Assimp::IOStream> &outfile,
@@ -175,31 +168,6 @@ public: // member functions for writing data to a file or stream
         bool has_children
     );
 
-private: // internal functions used for writing
-
-    void DumpBinary(Assimp::StreamWriterLE &s);
-    void DumpAscii(Assimp::StreamWriterLE &s, int indent);
-    void DumpAscii(std::ostream &s, int indent);
-
-    void BeginBinary(Assimp::StreamWriterLE &s);
-    void DumpPropertiesBinary(Assimp::StreamWriterLE& s);
-    void EndPropertiesBinary(Assimp::StreamWriterLE &s);
-    void EndPropertiesBinary(Assimp::StreamWriterLE &s, size_t num_properties);
-    void DumpChildrenBinary(Assimp::StreamWriterLE& s);
-    void EndBinary(Assimp::StreamWriterLE &s, bool has_children);
-
-    void BeginAscii(std::ostream &s, int indent);
-    void DumpPropertiesAscii(std::ostream &s, int indent);
-    void BeginChildrenAscii(std::ostream &s, int indent);
-    void DumpChildrenAscii(std::ostream &s, int indent);
-    void EndAscii(std::ostream &s, int indent, bool has_children);
-
-private: // data used for binary dumps
-    size_t start_pos; // starting position in stream
-    size_t end_pos; // ending position in stream
-    size_t property_start; // starting position of property section
-
-public: // static member functions
 
     // convenience function to create a node with a single property,
     // and write it to the stream.
@@ -235,7 +203,26 @@ public: // static member functions
         bool binary, int indent
     );
 
-private: // static helper functions
+private: // internal functions used for writing
+
+    void DumpBinary(Assimp::StreamWriterLE &s);
+    void DumpAscii(Assimp::StreamWriterLE &s, int indent);
+    void DumpAscii(std::ostream &s, int indent);
+
+    void BeginBinary(Assimp::StreamWriterLE &s);
+    void DumpPropertiesBinary(Assimp::StreamWriterLE& s);
+    void EndPropertiesBinary(Assimp::StreamWriterLE &s);
+    void EndPropertiesBinary(Assimp::StreamWriterLE &s, size_t num_properties);
+    void DumpChildrenBinary(Assimp::StreamWriterLE& s);
+    void EndBinary(Assimp::StreamWriterLE &s, bool has_children);
+
+    void BeginAscii(std::ostream &s, int indent);
+    void DumpPropertiesAscii(std::ostream &s, int indent);
+    void BeginChildrenAscii(std::ostream &s, int indent);
+    void DumpChildrenAscii(std::ostream &s, int indent);
+    void EndAscii(std::ostream &s, int indent, bool has_children);
+    
+    // static helper functions
     static void WritePropertyNodeAscii(
         const std::string& name,
         const std::vector<double>& v,
@@ -259,9 +246,13 @@ private: // static helper functions
         Assimp::StreamWriterLE& s
     );
 
+private: // data used for binary dumps
+    size_t start_pos; // starting position in stream
+    size_t end_pos; // ending position in stream
+    size_t property_start; // starting position of property section
 };
-}
 
-#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
+} // Namespace Assimp
 
+#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // AI_FBXEXPORTNODE_H_INC

+ 14 - 36
code/AssetLib/FBX/FBXExporter.cpp

@@ -370,12 +370,6 @@ void FBXExporter::WriteHeaderExtension ()
         "Creator", creator.str(), outstream, binary, indent
     );
 
-    //FBX::Node sceneinfo("SceneInfo");
-    //sceneinfo.AddProperty("GlobalInfo" + FBX::SEPARATOR + "SceneInfo");
-    // not sure if any of this is actually needed,
-    // so just write an empty node for now.
-    //sceneinfo.Dump(outstream, binary, indent);
-
     indent = 0;
 
     // finish node
@@ -459,11 +453,7 @@ void WritePropString(const aiScene* scene, FBX::Node& p, const std::string& key,
     }
 }
 
-void FBXExporter::WriteGlobalSettings ()
-{
-    if (!binary) {
-        // no title, follows directly from the header extension
-    }
+void FBXExporter::WriteGlobalSettings () {
     FBX::Node gs("GlobalSettings");
     gs.AddChild("Version", int32_t(1000));
 
@@ -493,8 +483,7 @@ void FBXExporter::WriteGlobalSettings ()
     gs.Dump(outfile, binary, 0);
 }
 
-void FBXExporter::WriteDocuments ()
-{
+void FBXExporter::WriteDocuments() {
     if (!binary) {
         WriteAsciiSectionHeader("Documents Description");
     }
@@ -523,8 +512,7 @@ void FBXExporter::WriteDocuments ()
     docs.Dump(outfile, binary, 0);
 }
 
-void FBXExporter::WriteReferences ()
-{
+void FBXExporter::WriteReferences() {
     if (!binary) {
         WriteAsciiSectionHeader("Document References");
     }
@@ -540,7 +528,6 @@ void FBXExporter::WriteReferences ()
 // some internal helper functions used for writing the definitions
 // (before any actual data is written)
 // ---------------------------------------------------------------
-
 size_t count_nodes(const aiNode* n, const aiNode* root) {
     size_t count;
     if (n == root) {
@@ -556,8 +543,7 @@ size_t count_nodes(const aiNode* n, const aiNode* root) {
     return count;
 }
 
-bool has_phong_mat(const aiScene* scene)
-{
+static bool has_phong_mat(const aiScene* scene) {
     // just search for any material with a shininess exponent
     for (size_t i = 0; i < scene->mNumMaterials; ++i) {
         aiMaterial* mat = scene->mMaterials[i];
@@ -570,16 +556,12 @@ bool has_phong_mat(const aiScene* scene)
     return false;
 }
 
-size_t count_images(const aiScene* scene) {
+static size_t count_images(const aiScene* scene) {
     std::unordered_set<std::string> images;
     aiString texpath;
     for (size_t i = 0; i < scene->mNumMaterials; ++i) {
-        aiMaterial* mat = scene->mMaterials[i];
-        for (
-            size_t tt = aiTextureType_DIFFUSE;
-            tt < aiTextureType_UNKNOWN;
-            ++tt
-        ){
+        aiMaterial *mat = scene->mMaterials[i];
+        for (size_t tt = aiTextureType_DIFFUSE; tt < aiTextureType_UNKNOWN; ++tt) {
             const aiTextureType textype = static_cast<aiTextureType>(tt);
             const size_t texcount = mat->GetTextureCount(textype);
             for (unsigned int j = 0; j < texcount; ++j) {
@@ -588,10 +570,11 @@ size_t count_images(const aiScene* scene) {
             }
         }
     }
+
     return images.size();
 }
 
-size_t count_textures(const aiScene* scene) {
+static size_t count_textures(const aiScene* scene) {
     size_t count = 0;
     for (size_t i = 0; i < scene->mNumMaterials; ++i) {
         aiMaterial* mat = scene->mMaterials[i];
@@ -609,7 +592,7 @@ size_t count_textures(const aiScene* scene) {
     return count;
 }
 
-size_t count_deformers(const aiScene* scene) {
+static size_t count_deformers(const aiScene* scene) {
     size_t count = 0;
     for (size_t i = 0; i < scene->mNumMeshes; ++i) {
         const size_t n = scene->mMeshes[i]->mNumBones;
@@ -621,8 +604,7 @@ size_t count_deformers(const aiScene* scene) {
     return count;
 }
 
-void FBXExporter::WriteDefinitions ()
-{
+void FBXExporter::WriteDefinitions () {
     // basically this is just bookkeeping:
     // determining how many of each type of object there are
     // and specifying the base properties to use when otherwise unspecified.
@@ -1033,9 +1015,7 @@ void FBXExporter::WriteDefinitions ()
 // some internal helper functions used for writing the objects section
 // (which holds the actual data)
 // -------------------------------------------------------------------
-
-aiNode* get_node_for_mesh(unsigned int meshIndex, aiNode* node)
-{
+static aiNode* get_node_for_mesh(unsigned int meshIndex, aiNode* node) {
     for (size_t i = 0; i < node->mNumMeshes; ++i) {
         if (node->mMeshes[i] == meshIndex) {
             return node;
@@ -1048,8 +1028,7 @@ aiNode* get_node_for_mesh(unsigned int meshIndex, aiNode* node)
     return nullptr;
 }
 
-aiMatrix4x4 get_world_transform(const aiNode* node, const aiScene* scene)
-{
+aiMatrix4x4 get_world_transform(const aiNode* node, const aiScene* scene) {
     std::vector<const aiNode*> node_chain;
     while (node != scene->mRootNode && node != nullptr) {
         node_chain.push_back(node);
@@ -1073,8 +1052,7 @@ inline int64_t to_ktime(double time) {
     return (static_cast<int64_t>(time * FBX::SECOND));
 }
 
-void FBXExporter::WriteObjects ()
-{
+void FBXExporter::WriteObjects () {
     if (!binary) {
         WriteAsciiSectionHeader("Object properties");
     }

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

@@ -51,7 +51,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "FBXCommon.h" // FBX::TransformInheritance
 
 #include <assimp/types.h>
-//#include <assimp/material.h>
 #include <assimp/StreamWriter.h> // StreamWriterLE
 #include <assimp/Exceptional.h> // DeadlyExportError
 

+ 0 - 4
code/AssetLib/FBX/FBXMaterial.cpp

@@ -134,10 +134,6 @@ Material::Material(uint64_t id, const Element& element, const Document& doc, con
     }
 }
 
-
-// ------------------------------------------------------------------------------------------------
-Material::~Material() = default;
-
 // ------------------------------------------------------------------------------------------------
 Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) :
         Object(id,element,name),

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

@@ -84,8 +84,7 @@ using ElementCollection = std::pair<ElementMap::const_iterator,ElementMap::const
  *  As can be seen in this sample, elements can contain nested #Scope
  *  as their trailing member.  
 **/
-class Element
-{
+class Element {
 public:
     Element(const Token& key_token, Parser& parser);
     ~Element();

+ 4 - 16
code/AssetLib/FBX/FBXProperties.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2024, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -57,15 +56,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 namespace Assimp {
 namespace FBX {
 
-    using namespace Util;
+using namespace Util;
 
 // ------------------------------------------------------------------------------------------------
-    Property::Property() = default;
-
-    // ------------------------------------------------------------------------------------------------
-    Property::~Property() = default;
 
-    namespace {
+namespace {
 
     void checkTokenCount(const TokenList &tok, unsigned int expectedCount) {
         ai_assert(expectedCount >= 2);
@@ -146,9 +141,9 @@ Property* ReadTypedProperty(const Element& element)
 
 // ------------------------------------------------------------------------------------------------
 // peek into an element and check if it contains a FBX property, if so return its name.
-std::string PeekPropertyName(const Element& element)
-{
+std::string PeekPropertyName(const Element& element) {
     ai_assert(element.KeyToken().StringContents() == "P");
+
     const TokenList& tok = element.Tokens();
     if(tok.size() < 4) {
         return std::string();
@@ -160,13 +155,6 @@ std::string PeekPropertyName(const Element& element)
 } //! anon
 
 
-// ------------------------------------------------------------------------------------------------
-PropertyTable::PropertyTable()
-: templateProps()
-, element()
-{
-}
-
 // ------------------------------------------------------------------------------------------------
 PropertyTable::PropertyTable(const Element &element, std::shared_ptr<const PropertyTable> templateProps) :
         templateProps(std::move(templateProps)), element(&element) {

+ 20 - 25
code/AssetLib/FBX/FBXProperties.h

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2024, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -56,34 +55,33 @@ namespace FBX {
 // Forward declarations
 class Element;
 
-/** Represents a dynamic property. Type info added by deriving classes,
- *  see #TypedProperty.
- Example:
- @verbatim
-   P: "ShininessExponent", "double", "Number", "",0.5
- @endvebatim
-*/
+/**
+ * Represents a dynamic property. Type info added by deriving classes,
+ * see #TypedProperty.
+ * Example:
+ *
+ * @verbatim
+ *  P: "ShininessExponent", "double", "Number", "",0.5
+ * @endvebatim
+ */
 class Property {
-protected:
-    Property();
 
 public:
-    virtual ~Property();
+    virtual ~Property() = default;
 
-public:
     template <typename T>
     const T* As() const {
         return dynamic_cast<const T*>(this);
     }
+
+protected:
+    Property() = default;
 };
 
 template<typename T>
 class TypedProperty : public Property {
 public:
-    explicit TypedProperty(const T& value)
-    : value(value) {
-        // empty
-    }
+    explicit TypedProperty(const T& value) : value(value) {}
 
     const T& Value() const {
         return value;
@@ -93,10 +91,9 @@ private:
     T value;
 };
 
-
-typedef std::fbx_unordered_map<std::string,std::shared_ptr<Property> > DirectPropertyMap;
-typedef std::fbx_unordered_map<std::string,const Property*>            PropertyMap;
-typedef std::fbx_unordered_map<std::string,const Element*>             LazyPropertyMap;
+using DirectPropertyMap = std::fbx_unordered_map<std::string,std::shared_ptr<Property> >;
+using PropertyMap = std::fbx_unordered_map<std::string,const Property*>;
+using LazyPropertyMap = std::fbx_unordered_map<std::string,const Element*>;
 
 /**
  *  Represents a property table as can be found in the newer FBX files (Properties60, Properties70)
@@ -104,7 +101,7 @@ typedef std::fbx_unordered_map<std::string,const Element*>             LazyPrope
 class PropertyTable {
 public:
     // in-memory property table with no source element
-    PropertyTable();
+    PropertyTable() : element() {}
     PropertyTable(const Element& element, std::shared_ptr<const PropertyTable> templateProps);
     ~PropertyTable();
 
@@ -130,8 +127,7 @@ private:
 
 // ------------------------------------------------------------------------------------------------
 template <typename T>
-inline
-T PropertyGet(const PropertyTable& in, const std::string& name, const T& defaultValue) {
+inline T PropertyGet(const PropertyTable& in, const std::string& name, const T& defaultValue) {
     const Property* const prop = in.Get(name);
     if( nullptr == prop) {
         return defaultValue;
@@ -148,8 +144,7 @@ T PropertyGet(const PropertyTable& in, const std::string& name, const T& default
 
 // ------------------------------------------------------------------------------------------------
 template <typename T>
-inline
-T PropertyGet(const PropertyTable& in, const std::string& name, bool& result, bool useTemplate=false ) {
+inline T PropertyGet(const PropertyTable& in, const std::string& name, bool& result, bool useTemplate=false ) {
     const Property* prop = in.Get(name);
     if( nullptr == prop) {
         if ( ! useTemplate ) {

+ 0 - 5
code/AssetLib/FBX/FBXUtil.h

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2024, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -53,10 +52,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 namespace Assimp {
 namespace FBX {
 
-
 namespace Util {
 
-
 /** helper for std::for_each to delete all heap-allocated items in a container */
 template<typename T>
 struct delete_fun
@@ -88,7 +85,6 @@ const char* TokenTypeString(TokenType t);
  *  @return A string of the following format: " (offset 0x{offset}) "*/
 std::string GetOffsetText(size_t offset);
 
-
 /** Format log/error messages using a given line location in the source file.
  *
  *  @param line Line index, 1-based
@@ -96,7 +92,6 @@ std::string GetOffsetText(size_t offset);
  *  @return A string of the following format: " (line {line}, col {column}) "*/
 std::string GetLineAndColumnText(unsigned int line, unsigned int column);
 
-
 /** Format log/error messages using a given cursor token.
  *
  *  @param tok Token where parsing/processing stopped

+ 0 - 1
code/AssetLib/NFF/NFFLoader.h

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

+ 1 - 1
code/AssetLib/Obj/ObjExporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2020, assimp team
+Copyright (c) 2006-2024, assimp team
 
 
 All rights reserved.

+ 2 - 2
code/AssetLib/Obj/ObjExporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2020, assimp team
+Copyright (c) 2006-2024, assimp team
 
 
 All rights reserved.
@@ -62,7 +62,7 @@ namespace Assimp {
 // ------------------------------------------------------------------------------------------------
 /** Helper class to export a given scene to an OBJ file. */
 // ------------------------------------------------------------------------------------------------
-class ObjExporter {
+class ObjExporter final {
 public:
     /// Constructor for a specific scene to export
     ObjExporter(const char* filename, const aiScene* pScene, bool noMtl=false, const ExportProperties* props = nullptr);

+ 1 - 1
code/AssetLib/Obj/ObjFileImporter.h

@@ -61,7 +61,7 @@ struct Model;
 /// \class  ObjFileImporter
 /// \brief  Imports a waveform obj file
 // ------------------------------------------------------------------------------------------------
-class ObjFileImporter : public BaseImporter {
+class ObjFileImporter final : public BaseImporter {
 public:
     /// \brief  Default constructor
     ObjFileImporter();

+ 1 - 5
code/AssetLib/Obj/ObjFileMtlImporter.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2020, assimp team
+Copyright (c) 2006-2024, assimp team
 
 All rights reserved.
 
@@ -106,10 +106,6 @@ ObjFileMtlImporter::ObjFileMtlImporter(std::vector<char> &buffer,
     load();
 }
 
-// -------------------------------------------------------------------
-//  Destructor
-ObjFileMtlImporter::~ObjFileMtlImporter() = default;
-
 // -------------------------------------------------------------------
 //  Loads the material description
 void ObjFileMtlImporter::load() {

+ 2 - 5
code/AssetLib/Obj/ObjFileMtlImporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2020, assimp team
+Copyright (c) 2006-2024, assimp team
 
 All rights reserved.
 
@@ -71,13 +71,12 @@ public:
             ObjFile::Model *pModel);
 
     //! \brief  The class destructor
-    ~ObjFileMtlImporter();
+    ~ObjFileMtlImporter() = default;
 
     ObjFileMtlImporter(const ObjFileMtlImporter &rOther) = delete;
     ObjFileMtlImporter &operator=(const ObjFileMtlImporter &rOther) = delete;
 
 private:
-    /// Copy constructor, empty.
     /// Load the whole material description
     void load();
     /// Get color data.
@@ -109,8 +108,6 @@ private:
     std::vector<char> m_buffer;
 };
 
-// ------------------------------------------------------------------------------------------------
-
 } // Namespace Assimp
 
 #endif // OBJFILEMTLIMPORTER_H_INC

+ 2 - 2
code/AssetLib/Obj/ObjFileParser.cpp

@@ -111,8 +111,8 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
     //const unsigned int updateProgressEveryBytes = 100 * 1024;
     const unsigned int bytesToProcess = static_cast<unsigned int>(streamBuffer.size());
     const unsigned int progressTotal = bytesToProcess;
-    unsigned int processed = 0;
-    size_t lastFilePos(0);
+    unsigned int processed = 0u;
+    size_t lastFilePos = 0u;
 
     bool insideCstype = false;
     std::vector<char> buffer;

+ 7 - 4
code/AssetLib/Obj/ObjFileParser.h

@@ -55,18 +55,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp {
 
+// Forward declarations
 class ObjFileImporter;
 class IOSystem;
 class ProgressHandler;
 
+// ------------------------------------------------------------------------------------------------
 /// \class  ObjFileParser
 /// \brief  Parser for a obj waveform file
+// ------------------------------------------------------------------------------------------------
 class ASSIMP_API ObjFileParser {
 public:
-    static const size_t Buffersize = 4096;
-    typedef std::vector<char> DataArray;
-    typedef std::vector<char>::iterator DataArrayIt;
-    typedef std::vector<char>::const_iterator ConstDataArrayIt;
+    static constexpr size_t Buffersize = 4096;
+    using DataArray = std::vector<char>;
+    using DataArrayIt = std::vector<char>::iterator;
+    using ConstDataArrayIt = std::vector<char>::const_iterator;
 
     /// @brief  The default constructor.
     ObjFileParser();

+ 54 - 0
include/assimp/scene.h

@@ -165,6 +165,33 @@ struct ASSIMP_API aiNode {
     const aiNode* FindNode(const char* name) const;
     aiNode* FindNode(const char* name);
 
+    // ------------------------------------------------------------------------------------------------
+    // Helper to find the node associated with a bone in the scene
+    const aiNode *findBoneNode(const aiBone *bone) const {
+        if (bone == nullptr) {
+            return nullptr;
+        }
+
+        if (mName == bone->mName) {
+            return this;
+        }
+
+        for (unsigned int i = 0; i < mNumChildren; ++i) {
+            aiNode *aChild = mChildren[i];
+            if (aChild == nullptr) {
+                continue;
+            }
+
+            const aiNode *foundFromChild = nullptr;
+            foundFromChild = aChild->findBoneNode(bone);
+            if (foundFromChild) {
+                return foundFromChild;
+            }
+        }
+
+        return nullptr;
+    }
+
     /**
      * @brief   Will add new children.
      * @param   numChildren  Number of children to add.
@@ -441,6 +468,33 @@ struct ASSIMP_API aiScene {
         }
         return std::make_pair(nullptr, -1);
     }
+
+        /**
+     * @brief Will try to locate a bone described by its name.
+     * 
+     * @param name  The name to look for.
+     * @return The bone as a pointer.
+     */
+    inline aiBone *findBone(const aiString &name) const {
+        for (size_t m = 0; m < mNumMeshes; m++) {
+            aiMesh *mesh = mMeshes[m];
+            if (mesh == nullptr) {
+                continue;
+            }
+
+            for (size_t b = 0; b < mesh->mNumBones; b++) {
+                aiBone *bone = mesh->mBones[b];
+                if (bone == nullptr) {
+                    continue;
+                }
+                if (name == bone->mName) {
+                    return bone;
+                }
+            }
+        }
+        return nullptr;
+    }
+
 #endif // __cplusplus
 
     /**  Internal data, do not touch */