浏览代码

Merge branch 'master' into fix/collada_parser_sid

Kim Kulling 3 年之前
父节点
当前提交
4f6640278a
共有 37 个文件被更改,包括 716 次插入449 次删除
  1. 6 0
      .github/dependabot.yml
  2. 5 5
      .github/workflows/ccpp.yml
  3. 2 2
      .github/workflows/sanitizer.yml
  4. 2 1
      Build.md
  5. 1 1
      CMakeLists.txt
  6. 2 0
      code/AssetLib/3MF/3MFXmlTags.h
  7. 3 0
      code/AssetLib/3MF/D3MFOpcPackage.cpp
  8. 31 8
      code/AssetLib/3MF/XmlSerializer.cpp
  9. 5 0
      code/AssetLib/Collada/ColladaHelper.h
  10. 6 3
      code/AssetLib/Collada/ColladaLoader.cpp
  11. 1 0
      code/AssetLib/Collada/ColladaLoader.h
  12. 3 1
      code/AssetLib/DXF/DXFLoader.cpp
  13. 4 0
      code/AssetLib/FBX/FBXConverter.cpp
  14. 1 1
      code/AssetLib/Irr/IRRLoader.cpp
  15. 1 2
      code/AssetLib/LWO/LWOLoader.cpp
  16. 3 0
      code/AssetLib/LWS/LWSLoader.cpp
  17. 3 0
      code/AssetLib/MDL/MDLMaterialLoader.cpp
  18. 3 1
      code/AssetLib/NDO/NDOLoader.cpp
  19. 32 16
      code/AssetLib/Obj/ObjFileMtlImporter.cpp
  20. 2 1
      code/AssetLib/Obj/ObjFileParser.cpp
  21. 21 29
      code/AssetLib/OpenGEX/OpenGEXImporter.cpp
  22. 3 8
      code/AssetLib/OpenGEX/OpenGEXImporter.h
  23. 12 2
      code/AssetLib/Q3D/Q3DLoader.cpp
  24. 19 19
      code/AssetLib/STL/STLLoader.cpp
  25. 1 1
      code/AssetLib/STL/STLLoader.h
  26. 47 95
      code/AssetLib/Step/STEPFile.h
  27. 1 1
      code/AssetLib/glTF2/glTF2Exporter.cpp
  28. 10 3
      code/Common/DefaultIOStream.cpp
  29. 29 26
      code/Common/ScenePreprocessor.cpp
  30. 2 0
      code/Common/ZipArchiveIOSystem.cpp
  31. 1 1
      code/PostProcessing/EmbedTexturesProcess.cpp
  32. 4 0
      code/res/assimp.rc
  33. 1 1
      contrib/stb/stb_image.h
  34. 275 195
      include/assimp/XmlParser.h
  35. 132 0
      test/models/Collada/box_nested_animation.dae
  36. 38 25
      test/unit/utColladaImportExport.cpp
  37. 4 1
      test/unit/utOpenGEXImportExport.cpp

+ 6 - 0
.github/dependabot.yml

@@ -0,0 +1,6 @@
+version: 2
+updates:
+  - package-ecosystem: "github-actions"
+    directory: "/"
+    schedule:
+      interval: "weekly"

+ 5 - 5
.github/workflows/ccpp.yml

@@ -43,7 +43,7 @@ jobs:
             toolchain: ninja-vs-win64-cxx17
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     
     - uses: lukka/get-cmake@latest
     
@@ -64,21 +64,21 @@ jobs:
 
     - name: Checkout Hunter toolchains
       if: endsWith(matrix.name, 'hunter')
-      uses: actions/checkout@v2
+      uses: actions/checkout@v3
       with:
         repository: cpp-pm/polly
         path: cmake/polly
 
     - name: Remove contrib directory for Hunter builds
       if: contains(matrix.name, 'hunter')
-      uses: JesseTG/[email protected].2
+      uses: JesseTG/[email protected].3
       with:
         path: contrib
 
     - name: Cache DX SDK
       id: dxcache
       if: contains(matrix.name, 'windows')
-      uses: actions/cache@v2
+      uses: actions/cache@v3
       with:
         path: '${{ github.workspace }}/DX_SDK'
         key: ${{ runner.os }}-DX_SDK
@@ -122,7 +122,7 @@ jobs:
       run: cd build/bin && ./unit ${{ steps.hunter_extra_test_args.outputs.args }}
       shell: bash
 
-    - uses: actions/upload-artifact@v2
+    - uses: actions/upload-artifact@v3
       if: matrix.name == 'windows-msvc'
       with:
         name: 'assimp-bins-${{ matrix.name }}-${{ github.sha }}'

+ 2 - 2
.github/workflows/sanitizer.yml

@@ -11,7 +11,7 @@ jobs:
     name: adress-sanitizer
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - uses: lukka/get-cmake@latest    
     - uses: lukka/set-shell-env@v1
       with:
@@ -35,7 +35,7 @@ jobs:
     name: undefined-behavior-sanitizer
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - uses: lukka/get-cmake@latest    
     - uses: lukka/set-shell-env@v1
       with:

+ 2 - 1
Build.md

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

+ 1 - 1
CMakeLists.txt

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

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

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

+ 3 - 0
code/AssetLib/3MF/D3MFOpcPackage.cpp

@@ -186,6 +186,9 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) :
 D3MFOpcPackage::~D3MFOpcPackage() {
     mZipArchive->Close(mRootStream);
     delete mZipArchive;
+    for (auto tex : mEmbeddedTextures) {
+        delete tex;
+    }
 }
 
 IOStream *D3MFOpcPackage::RootStream() const {

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

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

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

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

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

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

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

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

+ 3 - 1
code/AssetLib/DXF/DXFLoader.cpp

@@ -368,7 +368,9 @@ void DXFImporter::ExpandBlockReferences(DXF::Block& bl,const DXF::BlockMap& bloc
         // XXX this would be the place to implement recursive expansion if needed.
         const DXF::Block& bl_src = *(*it).second;
 
-        for (std::shared_ptr<const DXF::PolyLine> pl_in : bl_src.lines) {
+        const size_t size = bl_src.lines.size(); // the size may increase in the loop
+        for (size_t i = 0; i < size; ++i) {
+            std::shared_ptr<const DXF::PolyLine> pl_in = bl_src.lines[i];
             if (!pl_in) {
                 ASSIMP_LOG_ERROR("DXF: PolyLine instance is nullptr, skipping.");
                 continue;

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

@@ -3377,6 +3377,10 @@ FBXConverter::KeyFrameListList FBXConverter::GetRotationKeyframeList(const std::
                             Keys->push_back(tnew);
                             Values->push_back(vnew);
                         }
+                        else {
+                            // Something broke
+                            break;
+                        }
                         tp = tnew;
                         vp = vnew;
                     }

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

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

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

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

+ 3 - 0
code/AssetLib/LWS/LWSLoader.cpp

@@ -313,6 +313,9 @@ void LWSImporter::SetupNodeName(aiNode *nd, LWS::NodeDesc &src) {
             std::string::size_type t = src.path.substr(s).find_last_of('.');
 
             nd->mName.length = ::ai_snprintf(nd->mName.data, MAXLEN, "%s_(%08X)", src.path.substr(s).substr(0, t).c_str(), combined);
+            if (nd->mName.length > MAXLEN) {
+                nd->mName.length = MAXLEN;
+            }
             return;
         }
     }

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

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

+ 3 - 1
code/AssetLib/NDO/NDOLoader.cpp

@@ -136,7 +136,9 @@ void NDOImporter::InternReadFile( const std::string& pFile,
         ASSIMP_LOG_INFO("NDO file format is 1.2");
     }
     else {
-        ASSIMP_LOG_WARN( "Unrecognized nendo file format version, continuing happily ... :", (head+6));
+        char buff[4] = {0};
+        memcpy(buff, head+6, 3);
+        ASSIMP_LOG_WARN( "Unrecognized nendo file format version, continuing happily ... :", buff);
     }
 
     reader.IncPtr(2); /* skip flags */

+ 32 - 16
code/AssetLib/Obj/ObjFileMtlImporter.cpp

@@ -126,17 +126,21 @@ void ObjFileMtlImporter::load() {
                 if (*m_DataIt == 'a') // Ambient color
                 {
                     ++m_DataIt;
-                    getColorRGBA(&m_pModel->m_pCurrentMaterial->ambient);
+                    if (m_pModel->m_pCurrentMaterial != nullptr)
+                        getColorRGBA(&m_pModel->m_pCurrentMaterial->ambient);
                 } else if (*m_DataIt == 'd') {
                     // Diffuse color
                     ++m_DataIt;
-                    getColorRGBA(&m_pModel->m_pCurrentMaterial->diffuse);
+                    if (m_pModel->m_pCurrentMaterial != nullptr)
+                        getColorRGBA(&m_pModel->m_pCurrentMaterial->diffuse);
                 } else if (*m_DataIt == 's') {
                     ++m_DataIt;
-                    getColorRGBA(&m_pModel->m_pCurrentMaterial->specular);
+                    if (m_pModel->m_pCurrentMaterial != nullptr)
+                        getColorRGBA(&m_pModel->m_pCurrentMaterial->specular);
                 } else if (*m_DataIt == 'e') {
                     ++m_DataIt;
-                    getColorRGBA(&m_pModel->m_pCurrentMaterial->emissive);
+                    if (m_pModel->m_pCurrentMaterial != nullptr)
+                        getColorRGBA(&m_pModel->m_pCurrentMaterial->emissive);
                 }
                 m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
             } break;
@@ -145,13 +149,15 @@ void ObjFileMtlImporter::load() {
                 // Material transmission color
                 if (*m_DataIt == 'f')  {
                     ++m_DataIt;
-                    getColorRGBA(&m_pModel->m_pCurrentMaterial->transparent);
+                    if (m_pModel->m_pCurrentMaterial != nullptr)
+                        getColorRGBA(&m_pModel->m_pCurrentMaterial->transparent);
                 } else if (*m_DataIt == 'r')  {
                     // Material transmission alpha value
                     ++m_DataIt;
                     ai_real d;
                     getFloatValue(d);
-                    m_pModel->m_pCurrentMaterial->alpha = static_cast<ai_real>(1.0) - d;
+                    if (m_pModel->m_pCurrentMaterial != nullptr)
+                        m_pModel->m_pCurrentMaterial->alpha = static_cast<ai_real>(1.0) - d;
                 }
                 m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
             } break;
@@ -162,7 +168,8 @@ void ObjFileMtlImporter::load() {
                 } else {
                     // Alpha value
                     ++m_DataIt;
-                    getFloatValue(m_pModel->m_pCurrentMaterial->alpha);
+                    if (m_pModel->m_pCurrentMaterial != nullptr)
+                        getFloatValue(m_pModel->m_pCurrentMaterial->alpha);
                     m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
                 }
             } break;
@@ -173,11 +180,13 @@ void ObjFileMtlImporter::load() {
                 switch (*m_DataIt) {
                     case 's': // Specular exponent
                         ++m_DataIt;
-                        getFloatValue(m_pModel->m_pCurrentMaterial->shineness);
+                        if (m_pModel->m_pCurrentMaterial != nullptr)
+                            getFloatValue(m_pModel->m_pCurrentMaterial->shineness);
                         break;
                     case 'i': // Index Of refraction
                         ++m_DataIt;
-                        getFloatValue(m_pModel->m_pCurrentMaterial->ior);
+                        if (m_pModel->m_pCurrentMaterial != nullptr)
+                            getFloatValue(m_pModel->m_pCurrentMaterial->ior);
                         break;
                     case 'e': // New material
                         createMaterial();
@@ -197,23 +206,28 @@ void ObjFileMtlImporter::load() {
                     {
                     case 'r':
                         ++m_DataIt;
-                        getFloatValue(m_pModel->m_pCurrentMaterial->roughness);
+                        if (m_pModel->m_pCurrentMaterial != nullptr)
+                            getFloatValue(m_pModel->m_pCurrentMaterial->roughness);
                         break;
                     case 'm':
                         ++m_DataIt;
-                        getFloatValue(m_pModel->m_pCurrentMaterial->metallic);
+                        if (m_pModel->m_pCurrentMaterial != nullptr)
+                            getFloatValue(m_pModel->m_pCurrentMaterial->metallic);
                         break;
                     case 's':
                         ++m_DataIt;
-                        getColorRGBA(m_pModel->m_pCurrentMaterial->sheen);
+                        if (m_pModel->m_pCurrentMaterial != nullptr)
+                            getColorRGBA(m_pModel->m_pCurrentMaterial->sheen);
                         break;
                     case 'c':
                         ++m_DataIt;
                         if (*m_DataIt == 'r') {
                             ++m_DataIt;
-                            getFloatValue(m_pModel->m_pCurrentMaterial->clearcoat_roughness);
+                            if (m_pModel->m_pCurrentMaterial != nullptr)
+                                getFloatValue(m_pModel->m_pCurrentMaterial->clearcoat_roughness);
                         } else {
-                            getFloatValue(m_pModel->m_pCurrentMaterial->clearcoat_thickness);
+                            if (m_pModel->m_pCurrentMaterial != nullptr)
+                                getFloatValue(m_pModel->m_pCurrentMaterial->clearcoat_thickness);
                         }
                         break;
                     }
@@ -232,7 +246,8 @@ void ObjFileMtlImporter::load() {
             case 'i': // Illumination model
             {
                 m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
-                getIlluminationModel(m_pModel->m_pCurrentMaterial->illumination_model);
+                if (m_pModel->m_pCurrentMaterial != nullptr)
+                    getIlluminationModel(m_pModel->m_pCurrentMaterial->illumination_model);
                 m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
             } break;
 
@@ -240,7 +255,8 @@ void ObjFileMtlImporter::load() {
             {
                 ++m_DataIt;
                 getFloatValue(m_pModel->m_pCurrentMaterial->anisotropy);
-                m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+                if (m_pModel->m_pCurrentMaterial != nullptr)
+                    m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
             } break;
 
             default: {

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

@@ -458,7 +458,8 @@ void ObjFileParser::getFace(aiPrimitiveType type) {
             iPos = 0;
         } else {
             //OBJ USES 1 Base ARRAYS!!!!
-            const int iVal(::atoi(&(*m_DataIt)));
+            std::string number(&(*m_DataIt), m_DataItEnd - m_DataIt);
+            const int iVal(::atoi(number.c_str()));
 
             // increment iStep position based off of the sign and # of digits
             int tmp = iVal;

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

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

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

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

+ 12 - 2
code/AssetLib/Q3D/Q3DLoader.cpp

@@ -129,10 +129,20 @@ void Q3DImporter::InternReadFile(const std::string &pFile,
     unsigned int numTextures = (unsigned int)stream.GetI4();
 
     std::vector<Material> materials;
-    materials.reserve(numMats);
+    try {
+        materials.reserve(numMats);
+    } catch(const std::bad_alloc&) {
+        ASSIMP_LOG_ERROR("Invalid alloc for materials.");
+        throw DeadlyImportError("Invalid Quick3D-file, material allocation failed.");
+    }
 
     std::vector<Mesh> meshes;
-    meshes.reserve(numMeshes);
+    try {
+        meshes.reserve(numMeshes);
+    } catch(const std::bad_alloc&) {
+        ASSIMP_LOG_ERROR("Invalid alloc for meshes.");
+        throw DeadlyImportError("Invalid Quick3D-file, mesh allocation failed.");
+    }
 
     // Allocate the scene root node
     pScene->mRootNode = new aiNode();

+ 19 - 19
code/AssetLib/STL/STLLoader.cpp

@@ -73,7 +73,7 @@ static const aiImporterDesc desc = {
 // 1) 80 byte header
 // 2) 4 byte face count
 // 3) 50 bytes per face
-static bool IsBinarySTL(const char *buffer, unsigned int fileSize) {
+static bool IsBinarySTL(const char *buffer, size_t fileSize) {
     if (fileSize < 84) {
         return false;
     }
@@ -92,7 +92,7 @@ static const char UnicodeBoundary = 127;
 // An ascii STL buffer will begin with "solid NAME", where NAME is optional.
 // Note: The "solid NAME" check is necessary, but not sufficient, to determine
 // if the buffer is ASCII; a binary header could also begin with "solid NAME".
-static bool IsAsciiSTL(const char *buffer, unsigned int fileSize) {
+static bool IsAsciiSTL(const char *buffer, size_t fileSize) {
     if (IsBinarySTL(buffer, fileSize))
         return false;
 
@@ -172,7 +172,7 @@ void STLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
         throw DeadlyImportError("Failed to open STL file ", pFile, ".");
     }
 
-    mFileSize = (unsigned int)file->FileSize();
+    mFileSize = file->FileSize();
 
     // allocate storage and copy the contents of the file to a memory buffer
     // (terminate it with zero)
@@ -233,7 +233,7 @@ void STLImporter::LoadASCIIFile(aiNode *root) {
 
     // try to guess how many vertices we could have
     // assume we'll need 160 bytes for each face
-    size_t sizeEstimate = std::max(1u, mFileSize / 160u) * 3;
+    size_t sizeEstimate = std::max(1ull, mFileSize / 160ull) * 3ull;
     positionBuffer.reserve(sizeEstimate);
     normalBuffer.reserve(sizeEstimate);
 
@@ -284,8 +284,6 @@ void STLImporter::LoadASCIIFile(aiNode *root) {
                     ASSIMP_LOG_WARN("STL: A new facet begins but the old is not yet complete");
                 }
                 faceVertexCounter = 0;
-                normalBuffer.push_back(aiVector3D());
-                aiVector3D *vn = &normalBuffer.back();
 
                 sz += 6;
                 SkipSpaces(&sz);
@@ -295,15 +293,17 @@ void STLImporter::LoadASCIIFile(aiNode *root) {
                     if (sz[6] == '\0') {
                         throw DeadlyImportError("STL: unexpected EOF while parsing facet");
                     }
+                    aiVector3D vn;
                     sz += 7;
                     SkipSpaces(&sz);
-                    sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn->x);
+                    sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn.x);
                     SkipSpaces(&sz);
-                    sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn->y);
+                    sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn.y);
                     SkipSpaces(&sz);
-                    sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn->z);
-                    normalBuffer.push_back(*vn);
-                    normalBuffer.push_back(*vn);
+                    sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn.z);
+                    normalBuffer.emplace_back(vn);
+                    normalBuffer.emplace_back(vn);
+                    normalBuffer.emplace_back(vn);
                 }
             } else if (!strncmp(sz, "vertex", 6) && ::IsSpaceOrNewLine(*(sz + 6))) { // vertex 1.50000 1.50000 0.00000
                 if (faceVertexCounter >= 3) {
@@ -315,7 +315,7 @@ void STLImporter::LoadASCIIFile(aiNode *root) {
                     }
                     sz += 7;
                     SkipSpaces(&sz);
-                    positionBuffer.push_back(aiVector3D());
+                    positionBuffer.emplace_back();
                     aiVector3D *vn = &positionBuffer.back();
                     sz = fast_atoreal_move<ai_real>(sz, (ai_real &)vn->x);
                     SkipSpaces(&sz);
@@ -439,7 +439,7 @@ bool STLImporter::LoadBinaryFile() {
     pMesh->mNumFaces = *((uint32_t *)sz);
     sz += 4;
 
-    if (mFileSize < 84 + pMesh->mNumFaces * 50) {
+    if (mFileSize < 84ull + pMesh->mNumFaces * 50ull) {
         throw DeadlyImportError("STL: file is too small to hold all facets");
     }
 
@@ -517,13 +517,13 @@ bool STLImporter::LoadBinaryFile() {
             const ai_real invVal((ai_real)1.0 / (ai_real)31.0);
             if (bIsMaterialise) // this is reversed
             {
-                clr->r = (color & 0x31u) * invVal;
-                clr->g = ((color & (0x31u << 5)) >> 5u) * invVal;
-                clr->b = ((color & (0x31u << 10)) >> 10u) * invVal;
+                clr->r = (color & 0x1fu) * invVal;
+                clr->g = ((color & (0x1fu << 5)) >> 5u) * invVal;
+                clr->b = ((color & (0x1fu << 10)) >> 10u) * invVal;
             } else {
-                clr->b = (color & 0x31u) * invVal;
-                clr->g = ((color & (0x31u << 5)) >> 5u) * invVal;
-                clr->r = ((color & (0x31u << 10)) >> 10u) * invVal;
+                clr->b = (color & 0x1fu) * invVal;
+                clr->g = ((color & (0x1fu << 5)) >> 5u) * invVal;
+                clr->r = ((color & (0x1fu << 10)) >> 10u) * invVal;
             }
             // assign the color to all vertices of the face
             *(clr + 1) = *clr;

+ 1 - 1
code/AssetLib/STL/STLLoader.h

@@ -109,7 +109,7 @@ protected:
     const char* mBuffer;
 
     /** Size of the file, in bytes */
-    unsigned int mFileSize;
+    size_t mFileSize;
 
     /** Output scene */
     aiScene* mScene;

+ 47 - 95
code/AssetLib/Step/STEPFile.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2020, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -59,7 +58,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #    pragma warning(disable : 4127 4456 4245 4512 )
 #endif // _MSC_VER
 
-//
 #if _MSC_VER > 1500 || (defined __GNUC___)
 #    define ASSIMP_STEP_USE_UNORDERED_MULTIMAP
 #else
@@ -99,13 +97,9 @@ namespace EXPRESS {
 class DataType;
 class UNSET; /*: public DataType */
 class ISDERIVED; /*: public DataType */
-//  class REAL;         /*: public DataType */
 class ENUM; /*: public DataType */
-//  class STRING;       /*: public DataType */
-//  class INTEGER;      /*: public DataType */
 class ENTITY; /*: public DataType */
 class LIST; /*: public DataType */
-//  class SELECT;       /*: public DataType */
 
 // a conversion schema is not exactly an EXPRESS schema, rather it
 // is a list of pointers to conversion functions to build up the
@@ -127,7 +121,8 @@ namespace STEP {
 
 // -------------------------------------------------------------------------------
 /** Exception class used by the STEP loading & parsing code. It is typically
-     *  coupled with a line number. */
+ *  coupled with a line number. 
+ */
 // -------------------------------------------------------------------------------
 struct SyntaxError : DeadlyImportError {
     enum : uint64_t {
@@ -139,8 +134,9 @@ struct SyntaxError : DeadlyImportError {
 
 // -------------------------------------------------------------------------------
 /** Exception class used by the STEP loading & parsing code when a type
-     *  error (i.e. an entity expects a string but receives a bool) occurs.
-     *  It is typically coupled with both an entity id and a line number.*/
+ *  error (i.e. an entity expects a string but receives a bool) occurs.
+ *  It is typically coupled with both an entity id and a line number.
+ */
 // -------------------------------------------------------------------------------
 struct TypeError : DeadlyImportError {
     enum : uint64_t {
@@ -167,10 +163,8 @@ public:
     typedef std::shared_ptr<const DataType> Out;
 
 public:
-    virtual ~DataType() {
-    }
+    virtual ~DataType() = default;
 
-public:
     template <typename T>
     const T &To() const {
         return dynamic_cast<const T &>(*this);
@@ -206,16 +200,14 @@ public:
 
 public:
     /** parse a variable from a string and set 'inout' to the character
-             *  behind the last consumed character. An optional schema enables,
-             *  if specified, automatic conversion of custom data types.
-             *
-             *  @throw SyntaxError
-             */
+     *  behind the last consumed character. An optional schema enables,
+     *  if specified, automatic conversion of custom data types.
+     *
+     *  @throw SyntaxError
+     */
     static std::shared_ptr<const EXPRESS::DataType> Parse(const char *&inout,
             uint64_t line = SyntaxError::LINE_NOT_SPECIFIED,
             const EXPRESS::ConversionSchema *schema = NULL);
-
-public:
 };
 
 typedef DataType SELECT;
@@ -238,7 +230,8 @@ private:
 };
 
 // -------------------------------------------------------------------------------
-/** Shared implementation for some of the primitive data type, i.e. int, float */
+/** Shared implementation for some of the primitive data type, i.e. int, float 
+ */
 // -------------------------------------------------------------------------------
 template <typename T>
 class PrimitiveDataType : public DataType {
@@ -247,7 +240,7 @@ public:
     // expose this data type to the user.
     typedef T Out;
 
-    PrimitiveDataType() {}
+    PrimitiveDataType() = default;
     PrimitiveDataType(const T &val) :
             val(val) {}
 
@@ -280,28 +273,18 @@ class ENUMERATION : public STRING {
 public:
     ENUMERATION(const std::string &val) :
             STRING(val) {}
-
-private:
 };
 
 typedef ENUMERATION BOOLEAN;
 
 // -------------------------------------------------------------------------------
-/** This is just a reference to an entity/object somewhere else */
+/** This is just a reference to an entity/object somewhere else 
+ */
 // -------------------------------------------------------------------------------
 class ENTITY : public PrimitiveDataType<uint64_t> {
 public:
-    ENTITY(uint64_t val) :
-            PrimitiveDataType<uint64_t>(val) {
-        ai_assert(val != 0);
-    }
-
-    ENTITY() :
-            PrimitiveDataType<uint64_t>(TypeError::ENTITY_NOT_SPECIFIED) {
-        // empty
-    }
-
-private:
+    ENTITY(uint64_t val) : PrimitiveDataType<uint64_t>(val) {}
+    ENTITY() : PrimitiveDataType<uint64_t>(TypeError::ENTITY_NOT_SPECIFIED) {}
 };
 
 // -------------------------------------------------------------------------------
@@ -319,7 +302,8 @@ public:
     }
 
 public:
-    /** @see DaraType::Parse */
+    /** @see DaraType::Parse 
+     */
     static std::shared_ptr<const EXPRESS::LIST> Parse(const char *&inout,
             uint64_t line = SyntaxError::LINE_NOT_SPECIFIED,
             const EXPRESS::ConversionSchema *schema = NULL);
@@ -331,29 +315,20 @@ private:
 
 class BINARY : public PrimitiveDataType<uint32_t> {
 public:
-    BINARY(uint32_t val) :
-            PrimitiveDataType<uint32_t>(val) {
-        // empty
-    }
-
-    BINARY() :
-            PrimitiveDataType<uint32_t>(TypeError::ENTITY_NOT_SPECIFIED_32) {
-        // empty
-    }
+    BINARY(uint32_t val) : PrimitiveDataType<uint32_t>(val) {}
+    BINARY() : PrimitiveDataType<uint32_t>(TypeError::ENTITY_NOT_SPECIFIED_32) {}
 };
 
 // -------------------------------------------------------------------------------
 /* Not exactly a full EXPRESS schema but rather a list of conversion functions
-         * to extract valid C++ objects out of a STEP file. Those conversion functions
-         * may, however, perform further schema validations. */
+ * to extract valid C++ objects out of a STEP file. Those conversion functions
+ * may, however, perform further schema validations. 
+ */
 // -------------------------------------------------------------------------------
 class ConversionSchema {
 public:
     struct SchemaEntry {
-        SchemaEntry(const char *name, ConvertObjectProc func) :
-                mName(name), mFunc(func) {
-            // empty
-        }
+        SchemaEntry(const char *name, ConvertObjectProc func) : mName(name), mFunc(func) {}
 
         const char *mName;
         ConvertObjectProc mFunc;
@@ -366,8 +341,7 @@ public:
         *this = schemas;
     }
 
-    ConversionSchema() {
-    }
+    ConversionSchema() = default;
 
     ConvertObjectProc GetConverterProc(const std::string &name) const {
         ConverterMap::const_iterator it = converters.find(name);
@@ -399,8 +373,9 @@ private:
 
 // ------------------------------------------------------------------------------
 /** Bundle all the relevant info from a STEP header, parts of which may later
-     *  be plainly dumped to the logfile, whereas others may help the caller pick an
-     *  appropriate loading strategy.*/
+ *  be plainly dumped to the logfile, whereas others may help the caller pick an
+ *  appropriate loading strategy.
+ */
 // ------------------------------------------------------------------------------
 struct HeaderInfo {
     std::string timestamp;
@@ -409,18 +384,14 @@ struct HeaderInfo {
 };
 
 // ------------------------------------------------------------------------------
-/** Base class for all concrete object instances */
+/** Base class for all concrete object instances 
+ */
 // ------------------------------------------------------------------------------
 class Object {
 public:
-    Object(const char *classname = "unknown") :
-            id(0), classname(classname) {
-        // empty
-    }
+    Object(const char *classname = "unknown") : id(0), classname(classname) {}
 
-    virtual ~Object() {
-        // empty
-    }
+    virtual ~Object() = default;
 
     // utilities to simplify casting to concrete types
     template <typename T>
@@ -469,26 +440,15 @@ size_t GenericFill(const STEP::DB &db, const EXPRESS::LIST &params, T *in);
 // ------------------------------------------------------------------------------
 template <typename TDerived, size_t arg_count>
 struct ObjectHelper : virtual Object {
-    ObjectHelper() :
-            aux_is_derived(0) {
-        // empty
-    }
+    ObjectHelper() : aux_is_derived(0) {}
 
     static Object *Construct(const STEP::DB &db, const EXPRESS::LIST &params) {
         // make sure we don't leak if Fill() throws an exception
         std::unique_ptr<TDerived> impl(new TDerived());
 
         // GenericFill<T> is undefined so we need to have a specialization
-        const size_t num_args = GenericFill<TDerived>(db, params, &*impl);
-        (void)num_args;
-
-        // the following check is commented because it will always trigger if
-        // parts of the entities are generated with dummy wrapper code.
-        // This is currently done to reduce the size of the loader
-        // code.
-        //if (num_args != params.GetSize() && impl->GetClassName() != "NotImplemented") {
-        //  DefaultLogger::get()->debug("STEP: not all parameters consumed");
-        //}
+        static_cast<void>(GenericFill<TDerived>(db, params, &*impl));
+
         return impl.release();
     }
 
@@ -502,15 +462,9 @@ struct ObjectHelper : virtual Object {
 // ------------------------------------------------------------------------------
 template <typename T>
 struct Maybe {
-    Maybe() :
-            have() {
-        // empty
-    }
+    Maybe() : have() {}
 
-    explicit Maybe(const T &ptr) :
-            ptr(ptr), have(true) {
-        // empty
-    }
+    explicit Maybe(const T &ptr) : ptr(ptr), have(true) {}
 
     void flag_invalid() {
         have = false;
@@ -557,7 +511,8 @@ private:
 
 // ------------------------------------------------------------------------------
 /** A LazyObject is created when needed. Before this happens, we just keep
-       the text line that contains the object definition. */
+ *  the text line that contains the object definition. 
+ */
 // -------------------------------------------------------------------------------
 class LazyObject {
     friend class DB;
@@ -649,10 +604,7 @@ inline bool operator==(const std::pair<uint64_t, std::shared_ptr<LazyObject>> &l
 template <typename T>
 struct Lazy {
     typedef Lazy Out;
-    Lazy(const LazyObject *obj = nullptr) :
-            obj(obj) {
-        // empty
-    }
+    Lazy(const LazyObject *obj = nullptr) : obj(obj) {}
 
     operator const T *() const {
         return obj->ToPtr<T>();
@@ -785,8 +737,9 @@ inline void GenericConvert(ListOf<T1, N1, N2> &a, const std::shared_ptr<const EX
 
 // ------------------------------------------------------------------------------
 /** Lightweight manager class that holds the map of all objects in a
-     *  STEP file. DB's are exclusively maintained by the functions in
-     *  STEPFileReader.h*/
+ *  STEP file. DB's are exclusively maintained by the functions in
+ *  STEPFileReader.h
+ */
 // -------------------------------------------------------------------------------
 class DB {
     friend DB *ReadFileHeader(std::shared_ptr<IOStream> stream);
@@ -873,7 +826,7 @@ public:
         if (it != objects_bytype.end() && (*it).second.size()) {
             return *(*it).second.begin();
         }
-        return NULL;
+        return nullptr;
     }
 
     // same, but raise an exception if the object doesn't exist and return a reference
@@ -965,7 +918,6 @@ private:
 #endif // _MSC_VER
 
 } // namespace STEP
-
 } // namespace Assimp
 
 #endif // INCLUDED_AI_STEPFILE_H

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

@@ -683,7 +683,7 @@ bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &she
     }
 
     // Default Sheen color factor {0,0,0} disables Sheen, so do not export
-    if (sheen.sheenColorFactor == defaultSheenFactor) {
+    if (sheen.sheenColorFactor[0] == defaultSheenFactor[0] && sheen.sheenColorFactor[1] == defaultSheenFactor[1] && sheen.sheenColorFactor[2] == defaultSheenFactor[2]) {
         return false;
     }
 

+ 10 - 3
code/Common/DefaultIOStream.cpp

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

+ 29 - 26
code/Common/ScenePreprocessor.cpp

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

+ 2 - 0
code/Common/ZipArchiveIOSystem.cpp

@@ -196,7 +196,9 @@ zlib_filefunc_def IOSystem2Unzip::get(IOSystem *pIOHandler) {
     zlib_filefunc_def mapping;
 
     mapping.zopen_file = (open_file_func)open;
+#ifdef _UNZ_H
     mapping.zopendisk_file = (opendisk_file_func)opendisk;
+#endif
     mapping.zread_file = (read_file_func)read;
     mapping.zwrite_file = (write_file_func)write;
     mapping.ztell_file = (tell_file_func)tell;

+ 1 - 1
code/PostProcessing/EmbedTexturesProcess.cpp

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

+ 4 - 0
code/res/assimp.rc

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

+ 1 - 1
contrib/stb/stb_image.h

@@ -4941,7 +4941,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
 {
    stbi_uc palette[1024], pal_img_n=0;
    stbi_uc has_trans=0, tc[3]={0};
-   stbi__uint16 tc16[3];
+   stbi__uint16 tc16[3]={0};
    stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;
    int first=1,k,interlace=0, color=0, is_iphone=0;
    stbi__context *s = z->s;

+ 275 - 195
include/assimp/XmlParser.h

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

文件差异内容过多而无法显示
+ 132 - 0
test/models/Collada/box_nested_animation.dae


+ 38 - 25
test/unit/utColladaImportExport.cpp

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

+ 4 - 1
test/unit/utOpenGEXImportExport.cpp

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

部分文件因为文件数量过多而无法显示