Explorar el Código

Merge branch 'master' into master

Kim Kulling hace 4 años
padre
commit
2acfdbe0c5
Se han modificado 50 ficheros con 1244 adiciones y 722 borrados
  1. 9 8
      CMakeLists.txt
  2. 0 17
      cmake-modules/FindIrrXML.cmake
  3. 0 0
      cmake-modules/HunterGate.cmake
  4. 19 0
      cmake-modules/assimp-hunter-config.cmake.in
  5. 0 0
      cmake-modules/assimp-plain-config.cmake.in
  6. 0 19
      cmake/assimp-hunter-config.cmake.in
  7. 3 8
      code/AssetLib/3DS/3DSConverter.cpp
  8. 1 1
      code/AssetLib/3DS/3DSLoader.cpp
  9. 9 0
      code/AssetLib/3DS/3DSLoader.h
  10. 165 0
      code/AssetLib/3MF/3MFTypes.h
  11. 9 1
      code/AssetLib/3MF/3MFXmlTags.h
  12. 15 507
      code/AssetLib/3MF/D3MFImporter.cpp
  13. 28 6
      code/AssetLib/3MF/D3MFImporter.h
  14. 58 11
      code/AssetLib/3MF/D3MFOpcPackage.cpp
  15. 10 4
      code/AssetLib/3MF/D3MFOpcPackage.h
  16. 594 0
      code/AssetLib/3MF/XmlSerializer.cpp
  17. 96 0
      code/AssetLib/3MF/XmlSerializer.h
  18. 1 1
      code/AssetLib/Assbin/AssbinFileWriter.cpp
  19. 27 15
      code/AssetLib/Assjson/json_exporter.cpp
  20. 10 9
      code/AssetLib/Collada/ColladaLoader.cpp
  21. 3 3
      code/AssetLib/Collada/ColladaParser.cpp
  22. 5 3
      code/AssetLib/FBX/FBXConverter.cpp
  23. 12 16
      code/AssetLib/FBX/FBXParser.cpp
  24. 7 7
      code/AssetLib/M3D/M3DImporter.cpp
  25. 2 2
      code/AssetLib/Obj/ObjFileImporter.cpp
  26. 2 5
      code/AssetLib/XGL/XGLLoader.cpp
  27. 1 1
      code/AssetLib/XGL/XGLLoader.h
  28. 1 0
      code/AssetLib/glTF/glTFExporter.cpp
  29. 1 1
      code/AssetLib/glTF2/glTF2Asset.inl
  30. 20 2
      code/AssetLib/glTF2/glTF2Importer.cpp
  31. 4 1
      code/CMakeLists.txt
  32. 13 0
      code/Common/Assimp.cpp
  33. 1 1
      code/Common/DefaultIOSystem.cpp
  34. 5 2
      code/Common/RemoveComments.cpp
  35. 3 0
      code/Common/ScenePreprocessor.cpp
  36. 9 3
      code/Material/MaterialSystem.cpp
  37. 1 1
      code/PostProcessing/CalcTangentsProcess.cpp
  38. 1 1
      code/PostProcessing/OptimizeGraph.cpp
  39. 1 1
      code/PostProcessing/PretransformVertices.cpp
  40. 1 1
      code/PostProcessing/SortByPTypeProcess.cpp
  41. 1 2
      contrib/openddlparser/code/OpenDDLExport.cpp
  42. 4 4
      include/assimp/Logger.hpp
  43. 1 1
      include/assimp/scene.h
  44. 27 6
      include/assimp/vector3.h
  45. 1 1
      samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp
  46. 12 1
      test/unit/ImportExport/utAssjsonImportExport.cpp
  47. 6 5
      test/unit/utglTF2ImportExport.cpp
  48. 4 3
      tools/assimp_view/Material.cpp
  49. 35 38
      tools/assimp_view/MaterialManager.h
  50. 6 3
      tools/assimp_view/MeshRenderer.cpp

+ 9 - 8
CMakeLists.txt

@@ -44,7 +44,7 @@ CMAKE_MINIMUM_REQUIRED( VERSION 3.10 )
 option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF)
 
 IF(ASSIMP_HUNTER_ENABLED)
-  include("cmake/HunterGate.cmake")
+  include("cmake-modules/HunterGate.cmake")
   HunterGate(
     URL "https://github.com/cpp-pm/hunter/archive/v0.23.311.tar.gz"
     SHA1 "1a82b9b73055879181cb1466b2ab5d48ee8ae410"
@@ -135,11 +135,11 @@ IF ( WIN32 )
   # Use subset of Windows.h
   ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
 
-  OPTION ( ASSIMP_BUILD_ASSIMP_VIEW
-    "If the Assimp view tool is built. (requires DirectX)"
-    OFF )
-
   IF(MSVC)
+    OPTION ( ASSIMP_BUILD_ASSIMP_VIEW
+      "If the Assimp view tool is built. (requires DirectX)"
+      OFF )
+
     OPTION( ASSIMP_INSTALL_PDB
       "Install MSVC debug files."
       ON )
@@ -265,8 +265,9 @@ ELSEIF(MSVC)
   ENDIF()
   # disable "elements of array '' will be default initialized" warning on MSVC2013
   IF(MSVC12)
-    ADD_COMPILE_OPTIONS(/wd4351)
+    ADD_COMPILE_OPTIONS(/wd4351)	
   ENDIF()
+  ADD_COMPILE_OPTIONS(/wd4244) #supress warning for double to float conversion if Double precission is activated
   SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od")
   SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi")
   SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /PDBALTPATH:%_PDB% /OPT:REF /OPT:ICF")
@@ -397,14 +398,14 @@ set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
 
 IF(ASSIMP_HUNTER_ENABLED)
   set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
-  set(CMAKE_CONFIG_TEMPLATE_FILE "cmake/assimp-hunter-config.cmake.in")
+  set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-modules/assimp-hunter-config.cmake.in")
   set(NAMESPACE "${PROJECT_NAME}::")
   set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
   set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
   set(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}Config.cmake")
 ELSE()
   set(CONFIG_INSTALL_DIR "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}")
-  set(CMAKE_CONFIG_TEMPLATE_FILE "cmake/assimp-plain-config.cmake.in")
+  set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-modules/assimp-plain-config.cmake.in")
   string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWERCASE)
   set(NAMESPACE "${PROJECT_NAME_LOWERCASE}::")
   set(TARGETS_EXPORT_NAME "${PROJECT_NAME_LOWERCASE}Targets")

+ 0 - 17
cmake-modules/FindIrrXML.cmake

@@ -1,17 +0,0 @@
-# Find IrrXMl from irrlicht project
-#
-# Find LibIrrXML headers and library
-#
-#   IRRXML_FOUND          - IrrXML found
-#   IRRXML_INCLUDE_DIR    - Headers location
-#   IRRXML_LIBRARY        - IrrXML main library
-
-find_path(IRRXML_INCLUDE_DIR irrXML.h
-    PATH_SUFFIXES include/irrlicht include/irrxml)
-find_library(IRRXML_LIBRARY IrrXML)
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(IrrXML REQUIRED_VARS IRRXML_INCLUDE_DIR IRRXML_LIBRARY)
-
-
-mark_as_advanced(IRRXML_INCLUDE_DIR IRRXML_LIBRARY)

+ 0 - 0
cmake/HunterGate.cmake → cmake-modules/HunterGate.cmake


+ 19 - 0
cmake-modules/assimp-hunter-config.cmake.in

@@ -0,0 +1,19 @@
+@PACKAGE_INIT@
+
+find_package(RapidJSON     CONFIG REQUIRED)
+find_package(ZLIB          CONFIG REQUIRED)
+find_package(utf8cpp       CONFIG REQUIRED)
+find_package(minizip       CONFIG REQUIRED)
+find_package(openddlparser CONFIG REQUIRED)
+find_package(poly2tri      CONFIG REQUIRED)
+find_package(polyclipping  CONFIG REQUIRED)
+find_package(zip           CONFIG REQUIRED)
+find_package(pugixml       CONFIG REQUIRED)
+find_package(stb           CONFIG REQUIRED)
+
+if(@ASSIMP_BUILD_DRACO@)
+  find_package(draco CONFIG REQUIRED)
+endif()
+
+include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
+check_required_components("@PROJECT_NAME@")

+ 0 - 0
cmake/assimp-plain-config.cmake.in → cmake-modules/assimp-plain-config.cmake.in


+ 0 - 19
cmake/assimp-hunter-config.cmake.in

@@ -1,19 +0,0 @@
-@PACKAGE_INIT@
-
-find_package(RapidJSON CONFIG REQUIRED)
-find_package(ZLIB CONFIG REQUIRED)
-find_package(utf8cpp CONFIG REQUIRED)
-find_package(minizip CONFIG REQUIRED)
-find_package(openddlparser CONFIG REQUIRED)
-find_package(poly2tri CONFIG REQUIRED)
-find_package(polyclipping CONFIG REQUIRED)
-find_package(zip CONFIG REQUIRED)
-find_package(pugixml CONFIG REQUIRED)
-find_package(stb CONFIG REQUIRED)
-
-if(@ASSIMP_BUILD_DRACO@)
-  find_package(draco CONFIG REQUIRED)
-endif()
-
-include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
-check_required_components("@PROJECT_NAME@")

+ 3 - 8
code/AssetLib/3DS/3DSConverter.cpp

@@ -68,8 +68,8 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() {
     unsigned int idx(NotSet);
     for (unsigned int i = 0; i < mScene->mMaterials.size(); ++i) {
         std::string s = mScene->mMaterials[i].mName;
-        for (std::string::iterator it = s.begin(); it != s.end(); ++it) {
-            *it = static_cast<char>(::tolower(static_cast<unsigned char>(*it)));
+        for (char & it : s) {
+            it = static_cast<char>(::tolower(static_cast<unsigned char>(it)));
         }
 
         if (std::string::npos == s.find("default")) continue;
@@ -79,12 +79,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() {
                 mScene->mMaterials[i].mDiffuse.r !=
                         mScene->mMaterials[i].mDiffuse.b) continue;
 
-        if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 ||
-                mScene->mMaterials[i].sTexBump.mMapName.length() != 0 ||
-                mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 ||
-                mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 ||
-                mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 ||
-                mScene->mMaterials[i].sTexShininess.mMapName.length() != 0) {
+        if (ContainsTextures(i)) {
             continue;
         }
         idx = i;

+ 1 - 1
code/AssetLib/3DS/3DSLoader.cpp

@@ -449,7 +449,7 @@ void Discreet3DSImporter::ParseChunk(const char *name, unsigned int num) {
         // Read the lense angle
         camera->mHorizontalFOV = AI_DEG_TO_RAD(stream->GetF4());
         if (camera->mHorizontalFOV < 0.001f) {
-            camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f);
+            camera->mHorizontalFOV = float(AI_DEG_TO_RAD(45.f));
         }
 
         // Now check for further subchunks

+ 9 - 0
code/AssetLib/3DS/3DSLoader.h

@@ -208,6 +208,15 @@ protected:
     */
     void ReplaceDefaultMaterial();
 
+    bool ContainsTextures(unsigned int i) const {
+        return !mScene->mMaterials[i].sTexDiffuse.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexBump.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexOpacity.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexEmissive.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexSpecular.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexShininess.mMapName.empty() ;
+    }
+
     // -------------------------------------------------------------------
     /** Convert the whole scene
     */

+ 165 - 0
code/AssetLib/3MF/3MFTypes.h

@@ -0,0 +1,165 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2021, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+
+#include <assimp/vector3.h>
+#include <assimp/matrix4x4.h>
+#include <assimp/ParsingUtils.h>
+#include <vector>
+#include <string>
+
+struct aiMaterial;
+struct aiMesh;
+
+namespace Assimp {
+namespace D3MF {
+
+enum class ResourceType {
+    RT_Object,
+    RT_BaseMaterials,
+    RT_EmbeddedTexture2D,
+    RT_Texture2DGroup,
+    RT_Unknown
+}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...)
+
+class Resource {
+public:
+    int mId;
+
+    Resource(int id) :
+            mId(id) {
+        // empty
+    }
+
+    virtual ~Resource() {
+        // empty
+    }
+
+    virtual ResourceType getType() const {
+        return ResourceType::RT_Unknown;
+    }
+};
+
+class EmbeddedTexture : public Resource {
+public:
+    std::string mPath;
+    std::string mContentType;
+    std::string mTilestyleU;
+    std::string mTilestyleV;
+    std::vector<char> mBuffer;
+
+    EmbeddedTexture(int id) :
+            Resource(id),
+            mPath(),
+            mContentType(),
+            mTilestyleU(),
+            mTilestyleV() {
+        // empty
+    }
+
+    ~EmbeddedTexture() = default;
+
+    ResourceType getType() const override {
+        return ResourceType::RT_EmbeddedTexture2D;
+    }
+};
+
+class Texture2DGroup : public Resource {
+public:
+    std::vector<aiVector2D> mTex2dCoords;
+    int mTexId;
+    Texture2DGroup(int id) :
+            Resource(id),
+            mTexId(-1) {
+        // empty
+    }
+
+    ~Texture2DGroup() = default;
+
+    ResourceType getType() const override {
+        return ResourceType::RT_Texture2DGroup;
+    }
+};
+
+class BaseMaterials : public Resource {
+public:
+    std::vector<unsigned int> mMaterialIndex;
+
+    BaseMaterials(int id) :
+            Resource(id),
+            mMaterialIndex() {
+        // empty
+    }
+
+    ~BaseMaterials() = default;
+
+    ResourceType getType() const override {
+        return ResourceType::RT_BaseMaterials;
+    }
+};
+
+struct Component {
+    int mObjectId;
+    aiMatrix4x4 mTransformation;
+};
+
+class Object : public Resource {
+public:
+    std::vector<aiMesh *> mMeshes;
+    std::vector<unsigned int> mMeshIndex;
+    std::vector<Component> mComponents;
+    std::string mName;
+
+    Object(int id) :
+            Resource(id),
+            mName(std::string("Object_") + ai_to_string(id)) {
+        // empty
+    }
+
+    ~Object() = default;
+
+    ResourceType getType() const override {
+        return ResourceType::RT_Object;
+    }
+};
+
+} // namespace D3MF
+} // namespace Assimp

+ 9 - 1
code/AssetLib/3MF/3MFXmlTags.h

@@ -80,13 +80,21 @@ namespace XmlTag {
     const char* const item = "item";
     const char* const objectid = "objectid";
     const char* const transform = "transform";
+    const char *const path = "path";
 
     // Material definitions
     const char* const basematerials = "basematerials";
-    const char* const basematerials_id = "id";
     const char* const basematerials_base = "base";
     const char* const basematerials_name = "name";
     const char* const basematerials_displaycolor = "displaycolor";
+    const char* const texture_2d = "m:texture2d";
+    const char *const texture_group = "m:texture2dgroup";
+    const char *const texture_content_type = "contenttype";
+    const char *const texture_tilestyleu = "tilestyleu";
+    const char *const texture_tilestylev = "tilestylev";
+    const char *const texture_2d_coord = "m:tex2coord";
+    const char *const texture_cuurd_u = "u";
+    const char *const texture_cuurd_v = "v";
 
     // Meta info tags
     const char* const CONTENT_TYPES_ARCHIVE = "[Content_Types].xml";

+ 15 - 507
code/AssetLib/3MF/D3MFImporter.cpp

@@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "D3MFImporter.h"
 #include "3MFXmlTags.h"
 #include "D3MFOpcPackage.h"
+#include "XmlSerializer.h"
 
 #include <assimp/StringComparison.h>
 #include <assimp/StringUtils.h>
@@ -61,513 +62,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <string>
 #include <vector>
 #include <iomanip>
-#include <string.h>
+#include <cstring>
 
 namespace Assimp {
-namespace D3MF {
-
-enum class ResourceType {
-    RT_Object,
-    RT_BaseMaterials,
-    RT_Unknown
-}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...)
-
-class Resource {
-public:
-    int mId;
-
-    Resource(int id) :
-            mId(id) {
-        // empty
-    }
-
-    virtual ~Resource() {
-        // empty
-    }
-
-    virtual ResourceType getType() const {
-        return ResourceType::RT_Unknown;
-    }
-};
-
-class BaseMaterials : public Resource {
-public:
-    std::vector<aiMaterial *> mMaterials;
-    std::vector<unsigned int> mMaterialIndex;
-
-    BaseMaterials(int id) :
-            Resource(id),
-            mMaterials(),
-            mMaterialIndex() {
-        // empty
-    }
-
-    ~BaseMaterials() = default;
-
-    ResourceType getType() const override {
-        return ResourceType::RT_BaseMaterials;
-    }
-};
-
-struct Component {
-    int mObjectId;
-    aiMatrix4x4 mTransformation;
-};
-
-class Object : public Resource {
-public:
-    std::vector<aiMesh *> mMeshes;
-    std::vector<unsigned int> mMeshIndex;
-    std::vector<Component> mComponents;
-    std::string mName;
-
-    Object(int id) :
-            Resource(id),
-            mName(std::string("Object_") + ai_to_string(id)) {
-        // empty
-    }
-
-    ~Object() = default;
-
-    ResourceType getType() const override {
-        return ResourceType::RT_Object;
-    }
-};
-
-class XmlSerializer {
-public:
-    XmlSerializer(XmlParser *xmlParser) :
-            mResourcesDictionnary(),
-            mMaterialCount(0),
-            mMeshCount(0),
-            mXmlParser(xmlParser) {
-        // empty
-    }
-
-    ~XmlSerializer() {
-        for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it ) {
-            delete it->second;
-        }
-    }
-
-    void ImportXml(aiScene *scene) {
-        if (nullptr == scene) {
-            return;
-        }
-
-        scene->mRootNode = new aiNode(XmlTag::RootTag);
-
-        XmlNode node = mXmlParser->getRootNode().child(XmlTag::model);
-        if (node.empty()) {
-            return;
-        }
-        XmlNode resNode = node.child(XmlTag::resources);
-        for (auto &currentNode : resNode.children()) {
-            const std::string currentNodeName = currentNode.name();
-            if (currentNodeName == XmlTag::object) {
-                ReadObject(currentNode);
-            } else if (currentNodeName == XmlTag::basematerials) {
-                ReadBaseMaterials(currentNode);
-            } else if (currentNodeName == XmlTag::meta) {
-                ReadMetadata(currentNode);
-            }
-        }
-
-        XmlNode buildNode = node.child(XmlTag::build);
-        for (auto &currentNode : buildNode.children()) {
-            const std::string currentNodeName = currentNode.name();
-            if (currentNodeName == XmlTag::item) {
-                int objectId = -1;
-                std::string transformationMatrixStr;
-                aiMatrix4x4 transformationMatrix;
-                getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId);
-                bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr);
-
-                auto it = mResourcesDictionnary.find(objectId);
-                if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) {
-                    Object *obj = static_cast<Object *>(it->second);
-                    if (hasTransform) {
-                        transformationMatrix = parseTransformMatrix(transformationMatrixStr);
-                    }
-
-                    addObjectToNode(scene->mRootNode, obj, transformationMatrix);
-                }
-            }
-        }
-
-        // import the metadata
-        if (!mMetaData.empty()) {
-            const size_t numMeta = mMetaData.size();
-            scene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(numMeta));
-            for (size_t i = 0; i < numMeta; ++i) {
-                aiString val(mMetaData[i].value);
-                scene->mMetaData->Set(static_cast<unsigned int>(i), mMetaData[i].name, val);
-            }
-        }
-
-        // import the meshes
-        scene->mNumMeshes = static_cast<unsigned int>(mMeshCount);
-        if (scene->mNumMeshes != 0) {
-            scene->mMeshes = new aiMesh *[scene->mNumMeshes]();
-            for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) {
-                if (it->second->getType() == ResourceType::RT_Object) {
-                    Object *obj = static_cast<Object *>(it->second);
-                    ai_assert(nullptr != obj);
-                    for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) {
-                        scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i];
-                    }
-                }
-            }
-        }
-
-        // import the materials
-        scene->mNumMaterials = mMaterialCount;
-        if (scene->mNumMaterials != 0) {
-            scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
-            for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) {
-                if (it->second->getType() == ResourceType::RT_BaseMaterials) {
-                    BaseMaterials *baseMaterials = static_cast<BaseMaterials *>(it->second);
-                    for (unsigned int i = 0; i < baseMaterials->mMaterials.size(); ++i) {
-                        scene->mMaterials[baseMaterials->mMaterialIndex[i]] = baseMaterials->mMaterials[i];
-                    }
-                }
-            }
-        }
-    }
-
-private:
-    void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) {
-        ai_assert(nullptr != obj);
-
-        aiNode *sceneNode = new aiNode(obj->mName);
-        sceneNode->mNumMeshes = static_cast<unsigned int>(obj->mMeshes.size());
-        sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes];
-        std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes);
-
-        sceneNode->mTransformation = nodeTransform;
-        if (nullptr != parent) {
-            parent->addChildren(1, &sceneNode);
-        }
-
-        for (size_t i = 0; i < obj->mComponents.size(); ++i) {
-            Component c = obj->mComponents[i];
-            auto it = mResourcesDictionnary.find(c.mObjectId);
-            if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) {
-                addObjectToNode(sceneNode, static_cast<Object *>(it->second), c.mTransformation);
-            }
-        }
-    }
-
-    bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) {
-        pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str());
-        if (!objectAttribute.empty()) {
-            value = objectAttribute.as_string();
-            return true;
-        }
-
-        return false;
-    }
-
-    bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) {
-        std::string strValue;
-        bool ret = getNodeAttribute(node, attribute, strValue);
-        if (ret) {
-            value = std::atoi(strValue.c_str());
-            return true;
-        }
-
-        return false;
-    }
-
-    aiMatrix4x4 parseTransformMatrix(std::string matrixStr) {
-        // split the string
-        std::vector<float> numbers;
-        std::string currentNumber;
-        for (size_t i = 0; i < matrixStr.size(); ++i) {
-            const char c = matrixStr[i];
-            if (c == ' ') {
-                if (currentNumber.size() > 0) {
-                    float f = std::stof(currentNumber);
-                    numbers.push_back(f);
-                    currentNumber.clear();
-                }
-            } else {
-                currentNumber.push_back(c);
-            }
-        }
-        if (currentNumber.size() > 0) {
-            const float f = std::stof(currentNumber);
-            numbers.push_back(f);
-        }
-
-        aiMatrix4x4 transformMatrix;
-        transformMatrix.a1 = numbers[0];
-        transformMatrix.b1 = numbers[1];
-        transformMatrix.c1 = numbers[2];
-        transformMatrix.d1 = 0;
-
-        transformMatrix.a2 = numbers[3];
-        transformMatrix.b2 = numbers[4];
-        transformMatrix.c2 = numbers[5];
-        transformMatrix.d2 = 0;
-
-        transformMatrix.a3 = numbers[6];
-        transformMatrix.b3 = numbers[7];
-        transformMatrix.c3 = numbers[8];
-        transformMatrix.d3 = 0;
-
-        transformMatrix.a4 = numbers[9];
-        transformMatrix.b4 = numbers[10];
-        transformMatrix.c4 = numbers[11];
-        transformMatrix.d4 = 1;
-
-        return transformMatrix;
-    }
-
-    void ReadObject(XmlNode &node) {
-        int id = -1, pid = -1, pindex = -1;
-        bool hasId = getNodeAttribute(node, XmlTag::id, id);
-        bool hasPid = getNodeAttribute(node, XmlTag::pid, pid);
-        bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex);
-        if (!hasId) {
-            return;
-        }
-
-        Object *obj = new Object(id);
-
-        for (XmlNode &currentNode : node.children()) {
-            const std::string &currentName = currentNode.name();
-            if (currentName == D3MF::XmlTag::mesh) {
-                auto mesh = ReadMesh(currentNode);
-                mesh->mName.Set(ai_to_string(id));
-
-                if (hasPid) {
-                    auto it = mResourcesDictionnary.find(pid);
-                    if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) {
-                        BaseMaterials *materials = static_cast<BaseMaterials *>(it->second);
-                        mesh->mMaterialIndex = materials->mMaterialIndex[pindex];
-                    }
-                }
-
-                obj->mMeshes.push_back(mesh);
-                obj->mMeshIndex.push_back(mMeshCount);
-                mMeshCount++;
-            } else if (currentName == D3MF::XmlTag::components) {
-                for (XmlNode &currentSubNode : currentNode.children()) {
-                    const std::string subNodeName = currentSubNode.name();
-                    if (subNodeName == D3MF::XmlTag::component) {
-                        int objectId = -1;
-                        std::string componentTransformStr;
-                        aiMatrix4x4 componentTransform;
-                        if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) {
-                            componentTransform = parseTransformMatrix(componentTransformStr);
-                        }
-
-                        if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) {
-                            obj->mComponents.push_back({ objectId, componentTransform });
-                        }
-                    }
-                }
-            }
-        }
-
-        mResourcesDictionnary.insert(std::make_pair(id, obj));
-    }
-
-    aiMesh *ReadMesh(XmlNode &node) {
-        aiMesh *mesh = new aiMesh();
-
-        for (XmlNode &currentNode : node.children()) {
-            const std::string currentName = currentNode.name();
-            if (currentName == XmlTag::vertices) {
-                ImportVertices(currentNode, mesh);
-            } else if (currentName == XmlTag::triangles) {
-                ImportTriangles(currentNode, mesh);
-            }
-        }
-
-        return mesh;
-    }
-
-    void ReadMetadata(XmlNode &node) {
-        pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name);
-        const std::string name = attribute.as_string();
-        const std::string value = node.value();
-        if (name.empty()) {
-            return;
-        }
-
-        MetaEntry entry;
-        entry.name = name;
-        entry.value = value;
-        mMetaData.push_back(entry);
-    }
-
-    void ImportVertices(XmlNode &node, aiMesh *mesh) {
-        std::vector<aiVector3D> vertices;
-        for (XmlNode &currentNode : node.children()) {
-            const std::string currentName = currentNode.name();
-            if (currentName == XmlTag::vertex) {
-                vertices.push_back(ReadVertex(currentNode));
-            }
-        }
-
-        mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
-        mesh->mVertices = new aiVector3D[mesh->mNumVertices];
-        std::copy(vertices.begin(), vertices.end(), mesh->mVertices);
-    }
-
-    aiVector3D ReadVertex(XmlNode &node) {
-        aiVector3D vertex;
-        vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr);
-        vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr);
-        vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr);
-
-        return vertex;
-    }
-
-    void ImportTriangles(XmlNode &node, aiMesh *mesh) {
-        std::vector<aiFace> faces;
-        for (XmlNode &currentNode : node.children()) {
-            const std::string currentName = currentNode.name();
-            if (currentName == XmlTag::triangle) {
-                aiFace face = ReadTriangle(currentNode);
-                faces.push_back(face);
-
-                int pid = 0, p1 = 0;
-                bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid);
-                bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1);
-
-                if (hasPid && hasP1) {
-                    auto it = mResourcesDictionnary.find(pid);
-                    if (it != mResourcesDictionnary.end()) {
-                        if (it->second->getType() == ResourceType::RT_BaseMaterials) {
-                            BaseMaterials *baseMaterials = static_cast<BaseMaterials *>(it->second);
-                            mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1];
-                        }
-                        // TODO: manage the separation into several meshes if the triangles of the mesh do not all refer to the same material
-                    }
-                }
-            }
-        }
-
-        mesh->mNumFaces = static_cast<unsigned int>(faces.size());
-        mesh->mFaces = new aiFace[mesh->mNumFaces];
-        mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
-
-        std::copy(faces.begin(), faces.end(), mesh->mFaces);
-    }
-
-    aiFace ReadTriangle(XmlNode &node) {
-        aiFace face;
-
-        face.mNumIndices = 3;
-        face.mIndices = new unsigned int[face.mNumIndices];
-        face.mIndices[0] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v1).as_string()));
-        face.mIndices[1] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v2).as_string()));
-        face.mIndices[2] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v3).as_string()));
-
-        return face;
-    }
-
-    void ReadBaseMaterials(XmlNode &node) {
-        int id = -1;
-        if (getNodeAttribute(node, D3MF::XmlTag::basematerials_id, id)) {
-            BaseMaterials *baseMaterials = new BaseMaterials(id);
-
-            for (XmlNode &currentNode : node.children()) {
-                const std::string currentName = currentNode.name();
-                if (currentName == XmlTag::basematerials_base) {
-                    baseMaterials->mMaterialIndex.push_back(mMaterialCount);
-                    baseMaterials->mMaterials.push_back(readMaterialDef(currentNode, id));
-                    ++mMaterialCount;
-                }
-            }
-
-            mResourcesDictionnary.insert(std::make_pair(id, baseMaterials));
-        }
-    }
-
-    bool parseColor(const char *color, aiColor4D &diffuse) {
-        if (nullptr == color) {
-            return false;
-        }
-
-        //format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1)
-        const size_t len = strlen(color);
-        if (9 != len && 7 != len) {
-            return false;
-        }
-
-        const char *buf(color);
-        if ('#' != buf[0]) {
-            return false;
-        }
-
-        char r[3] = { buf[1], buf[2], '\0' };
-        diffuse.r = static_cast<ai_real>(strtol(r, nullptr, 16)) / ai_real(255.0);
-
-        char g[3] = { buf[3], buf[4], '\0' };
-        diffuse.g = static_cast<ai_real>(strtol(g, nullptr, 16)) / ai_real(255.0);
-
-        char b[3] = { buf[5], buf[6], '\0' };
-        diffuse.b = static_cast<ai_real>(strtol(b, nullptr, 16)) / ai_real(255.0);
-
-        if (7 == len)
-            return true;
-
-        char a[3] = { buf[7], buf[8], '\0' };
-        diffuse.a = static_cast<ai_real>(strtol(a, nullptr, 16)) / ai_real(255.0);
-
-        return true;
-    }
-
-    void assignDiffuseColor(XmlNode &node, aiMaterial *mat) {
-        const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string();
-        aiColor4D diffuse;
-        if (parseColor(color, diffuse)) {
-            mat->AddProperty<aiColor4D>(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
-        }
-    }
-
-    aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId) {
-        aiMaterial *material = new aiMaterial();
-        material->mNumProperties = 0;
-        std::string name;
-        bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name);
-
-        std::string stdMaterialName;
-        const std::string strId(ai_to_string(basematerialsId));
-        stdMaterialName += "id";
-        stdMaterialName += strId;
-        stdMaterialName += "_";
-        if (hasName) {
-            stdMaterialName += std::string(name);
-        } else {
-            stdMaterialName += "basemat_";
-            stdMaterialName += ai_to_string(mMaterialCount - basematerialsId);
-        }
-
-        aiString assimpMaterialName(stdMaterialName);
-        material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME);
-
-        assignDiffuseColor(node, material);
-
-        return material;
-    }
-
-private:
-    struct MetaEntry {
-        std::string name;
-        std::string value;
-    };
-    std::vector<MetaEntry> mMetaData;
-    std::map<unsigned int, Resource *> mResourcesDictionnary;
-    unsigned int mMaterialCount, mMeshCount;
-    XmlParser *mXmlParser;
-};
-
-} //namespace D3MF
 
 using namespace D3MF;
 
@@ -597,7 +94,9 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo
     const std::string extension(GetExtension(filename));
     if (extension == desc.mFileExtensions) {
         return true;
-    } else if (!extension.length() || checkSig) {
+    } 
+
+    if (!extension.length() || checkSig) {
         if (nullptr == pIOHandler) {
             return false;
         }
@@ -611,7 +110,7 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo
     return false;
 }
 
-void D3MFImporter::SetupProperties(const Importer * /*pImp*/) {
+void D3MFImporter::SetupProperties(const Importer*) {
     // empty
 }
 
@@ -626,6 +125,15 @@ void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene,
     if (xmlParser.parse(opcPackage.RootStream())) {
         XmlSerializer xmlSerializer(&xmlParser);
         xmlSerializer.ImportXml(pScene);
+
+        const std::vector<aiTexture*> &tex =  opcPackage.GetEmbeddedTextures();
+        if (!tex.empty()) {
+            pScene->mNumTextures = static_cast<unsigned int>(tex.size());
+            pScene->mTextures = new aiTexture *[pScene->mNumTextures];
+            for (unsigned int i = 0; i < pScene->mNumTextures; ++i) {
+                pScene->mTextures[i] = tex[i];
+            }
+        }
     }
 }
 

+ 28 - 6
code/AssetLib/3MF/D3MFImporter.h

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2021, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -47,17 +46,40 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp {
 
+// ---------------------------------------------------------------------------
 /// @brief  The 3MF-importer class.
+///
+/// Implements the basic topology import and embedded textures.
+// ---------------------------------------------------------------------------
 class D3MFImporter : public BaseImporter {
 public:
+    /// @brief The default class constructor.
     D3MFImporter();
-    ~D3MFImporter();
-    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const;
-    void SetupProperties(const Importer *pImp);
-    const aiImporterDesc *GetInfo() const;
+
+    ///	@brief  The class destructor.
+    ~D3MFImporter() override;
+
+    /// @brief Performs the data format detection.
+    /// @param pFile        The filename to check.
+    /// @param pIOHandler   The used IO-System.
+    /// @param checkSig     true for signature checking.
+    /// @return true for can be loaded, false for not.
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
+
+    /// @brief  Not used
+    /// @param pImp Not used
+    void SetupProperties(const Importer *pImp) override;
+
+    /// @brief The importer description getter.
+    /// @return The info
+    const aiImporterDesc *GetInfo() const override;
 
 protected:
-    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
+    /// @brief Internal read function, performs the file parsing.
+    /// @param pFile        The filename
+    /// @param pScene       The scene to load in.
+    /// @param pIOHandler   The io-system
+    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
 };
 
 } // Namespace Assimp

+ 58 - 11
code/AssetLib/3MF/D3MFOpcPackage.cpp

@@ -43,14 +43,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "D3MFOpcPackage.h"
 #include <assimp/Exceptional.h>
-
 #include <assimp/XmlParser.h>
 #include <assimp/ZipArchiveIOSystem.h>
 #include <assimp/ai_assert.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/IOStream.hpp>
 #include <assimp/IOSystem.hpp>
-
+#include <assimp/texture.h>
 #include "3MFXmlTags.h"
 #include <algorithm>
 #include <cassert>
@@ -64,11 +63,12 @@ namespace Assimp {
 namespace D3MF {
 // ------------------------------------------------------------------------------------------------
 
-typedef std::shared_ptr<OpcPackageRelationship> OpcPackageRelationshipPtr;
+using OpcPackageRelationshipPtr = std::shared_ptr<OpcPackageRelationship>;
 
 class OpcPackageRelationshipReader {
 public:
-    OpcPackageRelationshipReader(XmlParser &parser) {
+    OpcPackageRelationshipReader(XmlParser &parser) :
+            m_relationShips() {
         XmlNode root = parser.getRootNode();
         ParseRootNode(root);
     }
@@ -91,6 +91,7 @@ public:
         if (relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty()) {
             return false;
         }
+
         return true;
     }
 
@@ -100,7 +101,7 @@ public:
         }
 
         for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
-            std::string name = currentNode.name();
+            const std::string name = currentNode.name();
             if (name == "Relationship") {
                 OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship());
                 relPtr->id = currentNode.attribute(XmlTag::RELS_ATTRIB_ID).as_string();
@@ -116,11 +117,23 @@ public:
     std::vector<OpcPackageRelationshipPtr> m_relationShips;
 };
 
+static bool IsEmbeddedTexture( const std::string &filename ) {
+    const std::string extension = BaseImporter::GetExtension(filename);
+    if (extension == "jpg" || extension == "png") {
+        std::string::size_type pos = filename.find("thumbnail");
+        if (pos == std::string::npos) {
+            return false;
+        }
+        return true;
+    }
+
+    return false;
+}
 // ------------------------------------------------------------------------------------------------
 D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) :
         mRootStream(nullptr),
         mZipArchive() {
-    mZipArchive.reset(new ZipArchiveIOSystem(pIOHandler, rFile));
+    mZipArchive = new ZipArchiveIOSystem(pIOHandler, rFile);
     if (!mZipArchive->isOpen()) {
         throw DeadlyImportError("Failed to open file ", rFile, ".");
     }
@@ -141,13 +154,13 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) :
             }
 
             std::string rootFile = ReadPackageRootRelationship(fileStream);
-            if (rootFile.size() > 0 && rootFile[0] == '/') {
+            if (!rootFile.empty() && rootFile[0] == '/') {
                 rootFile = rootFile.substr(1);
                 if (rootFile[0] == '/') {
                     // deal with zip-bug
                     rootFile = rootFile.substr(1);
                 }
-            }
+            } 
 
             ASSIMP_LOG_VERBOSE_DEBUG(rootFile);
 
@@ -158,9 +171,12 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) :
             if (nullptr == mRootStream) {
                 throw DeadlyImportError("Cannot open root-file in archive : " + rootFile);
             }
-
         } else if (file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) {
             ASSIMP_LOG_WARN("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES", file);
+        } else if (IsEmbeddedTexture(file)) {
+            IOStream *fileStream = mZipArchive->Open(file.c_str());
+            LoadEmbeddedTextures(fileStream, file);
+            mZipArchive->Close(fileStream);
         } else {
             ASSIMP_LOG_WARN("Ignored file of unknown type: ", file);
         }
@@ -169,20 +185,26 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) :
 
 D3MFOpcPackage::~D3MFOpcPackage() {
     mZipArchive->Close(mRootStream);
+    delete mZipArchive;
+    mZipArchive = nullptr;
 }
 
 IOStream *D3MFOpcPackage::RootStream() const {
     return mRootStream;
 }
 
-static const std::string ModelRef = "3D/3dmodel.model";
+const std::vector<aiTexture *> &D3MFOpcPackage::GetEmbeddedTextures() const {
+    return mEmbeddedTextures;
+}
+
+static const char *const ModelRef = "3D/3dmodel.model";
 
 bool D3MFOpcPackage::validate() {
     if (nullptr == mRootStream || nullptr == mZipArchive) {
         return false;
     }
 
-    return mZipArchive->Exists(ModelRef.c_str());
+    return mZipArchive->Exists(ModelRef);
 }
 
 std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) {
@@ -204,6 +226,31 @@ std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) {
     return (*itr)->target;
 }
 
+void D3MFOpcPackage::LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename) {
+    if (nullptr == fileStream) {
+        return;
+    }
+
+    const size_t size = fileStream->FileSize();
+    if (0 == size) {
+        return;
+    }
+
+    unsigned char *data = new unsigned char[size];
+    fileStream->Read(data, 1, size);
+    aiTexture *texture = new aiTexture;
+    std::string embName = "*" + filename;
+    texture->mFilename.Set(embName.c_str());
+    texture->mWidth = static_cast<unsigned int>(size);
+    texture->mHeight = 0;
+    texture->achFormatHint[0] = 'p';
+    texture->achFormatHint[1] = 'n';
+    texture->achFormatHint[2] = 'g';
+    texture->achFormatHint[3] = '\0';
+    texture->pcData = (aiTexel*) data;
+    mEmbeddedTextures.emplace_back(texture);
+}
+
 } // Namespace D3MF
 } // Namespace Assimp
 

+ 10 - 4
code/AssetLib/3MF/D3MFOpcPackage.h

@@ -46,8 +46,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <string>
 #include <assimp/IOSystem.hpp>
 
+struct aiTexture;
+
 namespace Assimp {
-    class ZipArchiveIOSystem;
+
+class ZipArchiveIOSystem;
 
 namespace D3MF {
 
@@ -63,16 +66,19 @@ public:
     ~D3MFOpcPackage();
     IOStream* RootStream() const;
     bool validate();
+    const std::vector<aiTexture*> &GetEmbeddedTextures() const;
 
 protected:
     std::string ReadPackageRootRelationship(IOStream* stream);
+    void LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename);
 
 private:
     IOStream* mRootStream;
-    std::unique_ptr<ZipArchiveIOSystem> mZipArchive;
+    ZipArchiveIOSystem *mZipArchive;
+    std::vector<aiTexture *> mEmbeddedTextures;
 };
 
-} // Namespace D3MF
-} // Namespace Assimp
+} // namespace D3MF
+} // namespace Assimp
 
 #endif // D3MFOPCPACKAGE_H

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

@@ -0,0 +1,594 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2021, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#include "XmlSerializer.h"
+#include "D3MFOpcPackage.h"
+#include "3MFXmlTags.h"
+#include "3MFTypes.h"
+#include <assimp/scene.h>
+
+namespace Assimp {
+namespace D3MF {
+
+static const int IdNotSet = -1;
+
+namespace {
+
+static const size_t ColRGBA_Len = 9;
+static const size_t ColRGB_Len = 7;
+
+// format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1)
+bool validateColorString(const char *color) {
+    const size_t len = strlen(color);
+    if (ColRGBA_Len != len && ColRGB_Len != len) {
+        return false;
+    }
+
+    return true;
+}
+
+aiFace ReadTriangle(XmlNode &node) {
+    aiFace face;
+
+    face.mNumIndices = 3;
+    face.mIndices = new unsigned int[face.mNumIndices];
+    face.mIndices[0] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v1).as_string()));
+    face.mIndices[1] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v2).as_string()));
+    face.mIndices[2] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v3).as_string()));
+
+    return face;
+}
+
+aiVector3D ReadVertex(XmlNode &node) {
+    aiVector3D vertex;
+    vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr);
+    vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr);
+    vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr);
+
+    return vertex;
+}
+
+bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) {
+    pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str());
+    if (!objectAttribute.empty()) {
+        value = objectAttribute.as_string();
+        return true;
+    }
+
+    return false;
+}
+
+bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) {
+    std::string strValue;
+    const bool ret = getNodeAttribute(node, attribute, strValue);
+    if (ret) {
+        value = std::atoi(strValue.c_str());
+        return true;
+    }
+
+    return false;
+}
+
+aiMatrix4x4 parseTransformMatrix(std::string matrixStr) {
+    // split the string
+    std::vector<float> numbers;
+    std::string currentNumber;
+    for (char c : matrixStr) {
+        if (c == ' ') {
+            if (!currentNumber.empty()) {
+                float f = std::stof(currentNumber);
+                numbers.push_back(f);
+                currentNumber.clear();
+            }
+        } else {
+            currentNumber.push_back(c);
+        }
+    }
+    if (!currentNumber.empty()) {
+        const float f = std::stof(currentNumber);
+        numbers.push_back(f);
+    }
+
+    aiMatrix4x4 transformMatrix;
+    transformMatrix.a1 = numbers[0];
+    transformMatrix.b1 = numbers[1];
+    transformMatrix.c1 = numbers[2];
+    transformMatrix.d1 = 0;
+
+    transformMatrix.a2 = numbers[3];
+    transformMatrix.b2 = numbers[4];
+    transformMatrix.c2 = numbers[5];
+    transformMatrix.d2 = 0;
+
+    transformMatrix.a3 = numbers[6];
+    transformMatrix.b3 = numbers[7];
+    transformMatrix.c3 = numbers[8];
+    transformMatrix.d3 = 0;
+
+    transformMatrix.a4 = numbers[9];
+    transformMatrix.b4 = numbers[10];
+    transformMatrix.c4 = numbers[11];
+    transformMatrix.d4 = 1;
+
+    return transformMatrix;
+}
+
+bool parseColor(const char *color, aiColor4D &diffuse) {
+    if (nullptr == color) {
+        return false;
+    }
+
+    if (!validateColorString(color)) {
+        return false;
+    }
+
+    //const char *buf(color);
+    if ('#' != color[0]) {
+        return false;
+    }
+
+    char r[3] = { color[1], color[2], '\0' };
+    diffuse.r = static_cast<ai_real>(strtol(r, nullptr, 16)) / ai_real(255.0);
+
+    char g[3] = { color[3], color[4], '\0' };
+    diffuse.g = static_cast<ai_real>(strtol(g, nullptr, 16)) / ai_real(255.0);
+
+    char b[3] = { color[5], color[6], '\0' };
+    diffuse.b = static_cast<ai_real>(strtol(b, nullptr, 16)) / ai_real(255.0);
+    const size_t len = strlen(color);
+    if (ColRGB_Len == len) {
+        return true;
+    }
+
+    char a[3] = { color[7], color[8], '\0' };
+    diffuse.a = static_cast<ai_real>(strtol(a, nullptr, 16)) / ai_real(255.0);
+
+    return true;
+}
+
+void assignDiffuseColor(XmlNode &node, aiMaterial *mat) {
+    const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string();
+    aiColor4D diffuse;
+    if (parseColor(color, diffuse)) {
+        mat->AddProperty<aiColor4D>(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+    }
+}
+
+} // namespace
+
+XmlSerializer::XmlSerializer(XmlParser *xmlParser) :
+        mResourcesDictionnary(),
+        mMeshCount(0),
+        mXmlParser(xmlParser) {
+    ai_assert(nullptr != xmlParser);
+}
+
+XmlSerializer::~XmlSerializer() {
+    for (auto &it : mResourcesDictionnary) {
+        delete it.second;
+    }
+}
+
+void XmlSerializer::ImportXml(aiScene *scene) {
+    if (nullptr == scene) {
+        return;
+    }
+    
+    scene->mRootNode = new aiNode(XmlTag::RootTag);
+    XmlNode node = mXmlParser->getRootNode().child(XmlTag::model);
+    if (node.empty()) {
+        return;
+    }
+
+    XmlNode resNode = node.child(XmlTag::resources);
+    for (auto &currentNode : resNode.children()) {
+        const std::string currentNodeName = currentNode.name();
+        if (currentNodeName == XmlTag::texture_2d) {
+            ReadEmbeddecTexture(currentNode);
+        } else if (currentNodeName == XmlTag::texture_group) {
+            ReadTextureGroup(currentNode);
+        } else if (currentNodeName == XmlTag::object) {
+            ReadObject(currentNode);
+        } else if (currentNodeName == XmlTag::basematerials) {
+            ReadBaseMaterials(currentNode);
+        } else if (currentNodeName == XmlTag::meta) {
+            ReadMetadata(currentNode);
+        }
+    }
+    StoreMaterialsInScene(scene);
+    XmlNode buildNode = node.child(XmlTag::build);
+    if (buildNode.empty()) {
+        return;
+    }
+
+    for (auto &currentNode : buildNode.children()) {
+        const std::string currentNodeName = currentNode.name();
+        if (currentNodeName == XmlTag::item) {
+            int objectId = IdNotSet;
+            std::string transformationMatrixStr;
+            aiMatrix4x4 transformationMatrix;
+            getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId);
+            bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr);
+
+            auto it = mResourcesDictionnary.find(objectId);
+            if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) {
+                Object *obj = static_cast<Object *>(it->second);
+                if (hasTransform) {
+                    transformationMatrix = parseTransformMatrix(transformationMatrixStr);
+                }
+
+                addObjectToNode(scene->mRootNode, obj, transformationMatrix);
+            }
+        }
+    }
+
+    // import the metadata
+    if (!mMetaData.empty()) {
+        const size_t numMeta = mMetaData.size();
+        scene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(numMeta));
+        for (size_t i = 0; i < numMeta; ++i) {
+            aiString val(mMetaData[i].value);
+            scene->mMetaData->Set(static_cast<unsigned int>(i), mMetaData[i].name, val);
+        }
+    }
+
+    // import the meshes, materials are already stored
+    scene->mNumMeshes = static_cast<unsigned int>(mMeshCount);
+    if (scene->mNumMeshes != 0) {
+        scene->mMeshes = new aiMesh *[scene->mNumMeshes]();
+        for (auto &it : mResourcesDictionnary) {
+            if (it.second->getType() == ResourceType::RT_Object) {
+                Object *obj = static_cast<Object *>(it.second);
+                ai_assert(nullptr != obj);
+                for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) {
+                    scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i];
+                }
+            }
+        }
+    }
+}
+
+void XmlSerializer::addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) {
+    ai_assert(nullptr != obj);
+
+    aiNode *sceneNode = new aiNode(obj->mName);
+    sceneNode->mNumMeshes = static_cast<unsigned int>(obj->mMeshes.size());
+    sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes];
+    std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes);
+
+    sceneNode->mTransformation = nodeTransform;
+    if (nullptr != parent) {
+        parent->addChildren(1, &sceneNode);
+    }
+
+    for (Assimp::D3MF::Component c : obj->mComponents) {
+        auto it = mResourcesDictionnary.find(c.mObjectId);
+        if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) {
+            addObjectToNode(sceneNode, static_cast<Object *>(it->second), c.mTransformation);
+        }
+    }
+}
+
+void XmlSerializer::ReadObject(XmlNode &node) {
+    int id = IdNotSet, pid = IdNotSet, pindex = IdNotSet;
+    bool hasId = getNodeAttribute(node, XmlTag::id, id);
+    if (!hasId) {
+        return;
+    }
+
+    bool hasPid = getNodeAttribute(node, XmlTag::pid, pid);
+    bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex);
+
+    Object *obj = new Object(id);
+    for (XmlNode &currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        if (currentName == D3MF::XmlTag::mesh) {
+            auto mesh = ReadMesh(currentNode);
+            mesh->mName.Set(ai_to_string(id));
+
+            if (hasPid) {
+                auto it = mResourcesDictionnary.find(pid);
+                if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) {
+                    BaseMaterials *materials = static_cast<BaseMaterials *>(it->second);
+                    mesh->mMaterialIndex = materials->mMaterialIndex[pindex];
+                }
+            }
+
+            obj->mMeshes.push_back(mesh);
+            obj->mMeshIndex.push_back(mMeshCount);
+            mMeshCount++;
+        } else if (currentName == D3MF::XmlTag::components) {
+            for (XmlNode &currentSubNode : currentNode.children()) {
+                const std::string subNodeName = currentSubNode.name();
+                if (subNodeName == D3MF::XmlTag::component) {
+                    int objectId = IdNotSet;
+                    std::string componentTransformStr;
+                    aiMatrix4x4 componentTransform;
+                    if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) {
+                        componentTransform = parseTransformMatrix(componentTransformStr);
+                    }
+
+                    if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) {
+                        obj->mComponents.push_back({ objectId, componentTransform });
+                    }
+                }
+            }
+        }
+    }
+
+    mResourcesDictionnary.insert(std::make_pair(id, obj));
+}
+
+aiMesh *XmlSerializer::ReadMesh(XmlNode &node) {
+    if (node.empty()) {
+        return nullptr;
+    }
+
+    aiMesh *mesh = new aiMesh();
+    for (XmlNode &currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        if (currentName == XmlTag::vertices) {
+            ImportVertices(currentNode, mesh);
+        } else if (currentName == XmlTag::triangles) {
+            ImportTriangles(currentNode, mesh);
+        }
+    }
+
+    return mesh;
+}
+
+void XmlSerializer::ReadMetadata(XmlNode &node) {
+    pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name);
+    const std::string name = attribute.as_string();
+    const std::string value = node.value();
+    if (name.empty()) {
+        return;
+    }
+
+    MetaEntry entry;
+    entry.name = name;
+    entry.value = value;
+    mMetaData.push_back(entry);
+}
+
+void XmlSerializer::ImportVertices(XmlNode &node, aiMesh *mesh) {
+    ai_assert(nullptr != mesh);
+
+    std::vector<aiVector3D> vertices;
+    for (XmlNode &currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        if (currentName == XmlTag::vertex) {
+            vertices.push_back(ReadVertex(currentNode));
+        }
+    }
+
+    mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
+    mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+    std::copy(vertices.begin(), vertices.end(), mesh->mVertices);
+}
+
+void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) {
+    std::vector<aiFace> faces;
+    for (XmlNode &currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        if (currentName == XmlTag::triangle) {
+            int pid = IdNotSet, p1 = IdNotSet;
+            bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid);
+            bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1);
+
+            if (hasPid && hasP1) {
+                auto it = mResourcesDictionnary.find(pid);
+                if (it != mResourcesDictionnary.end()) {
+                    if (it->second->getType() == ResourceType::RT_BaseMaterials) {
+                        BaseMaterials *baseMaterials = static_cast<BaseMaterials *>(it->second);
+                        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);
+                            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);
+                            }
+                        }
+                    } 
+                }
+            }
+
+            aiFace face = ReadTriangle(currentNode);
+            faces.push_back(face);
+        }
+    }
+
+    mesh->mNumFaces = static_cast<unsigned int>(faces.size());
+    mesh->mFaces = new aiFace[mesh->mNumFaces];
+    mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+    std::copy(faces.begin(), faces.end(), mesh->mFaces);
+}
+
+void XmlSerializer::ReadBaseMaterials(XmlNode &node) {
+    int id = IdNotSet;
+    if (getNodeAttribute(node, D3MF::XmlTag::id, id)) {
+        BaseMaterials *baseMaterials = new BaseMaterials(id);
+
+        for (XmlNode &currentNode : node.children()) {
+            const std::string currentName = currentNode.name();
+            if (currentName == XmlTag::basematerials_base) {
+                baseMaterials->mMaterialIndex.push_back(static_cast<unsigned int>(mMaterials.size()));
+                mMaterials.push_back(readMaterialDef(currentNode, id));
+            }
+        }
+
+        mResourcesDictionnary.insert(std::make_pair(id, baseMaterials));
+    }
+}
+
+void XmlSerializer::ReadEmbeddecTexture(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    std::string value;
+    EmbeddedTexture *tex2D = nullptr;
+    if (XmlParser::getStdStrAttribute(node, XmlTag::id, value)) {
+        tex2D = new EmbeddedTexture(atoi(value.c_str()));
+    }
+    if (nullptr == tex2D) {
+        return;
+    }
+
+    if (XmlParser::getStdStrAttribute(node, XmlTag::path, value)) {
+        tex2D->mPath = value;
+    }
+    if (XmlParser::getStdStrAttribute(node, XmlTag::texture_content_type, value)) {
+        tex2D->mContentType = value;
+    }
+    if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestyleu, value)) {
+        tex2D->mTilestyleU = value;
+    }
+    if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestylev, value)) {
+        tex2D->mTilestyleV = value;
+    }
+    mEmbeddedTextures.emplace_back(tex2D);
+    StoreEmbeddedTexture(tex2D);
+}
+
+void XmlSerializer::StoreEmbeddedTexture(EmbeddedTexture *tex) {
+    aiMaterial *mat = new aiMaterial;
+    aiString s;
+    s.Set(ai_to_string(tex->mId).c_str());
+    mat->AddProperty(&s, AI_MATKEY_NAME);
+    const std::string name = "*" + tex->mPath;
+    s.Set(name);
+    mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+    aiColor3D col;
+    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_DIFFUSE);
+    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_AMBIENT);
+    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_EMISSIVE);
+    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_SPECULAR);
+    mMaterials.emplace_back(mat);
+}
+
+void XmlSerializer::ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup) {
+    if (node.empty() || nullptr == tex2DGroup) {
+        return;
+    }
+
+    int id = IdNotSet;
+    if (XmlParser::getIntAttribute(node, "texid", id)) {
+        tex2DGroup->mTexId = id;
+    }
+
+    double value = 0.0;
+    for (XmlNode currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        aiVector2D texCoord;
+        if (currentName == XmlTag::texture_2d_coord) {
+            XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_u, value);
+            texCoord.x = (ai_real)value;
+            XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_v, value);
+            texCoord.y = (ai_real)value;
+            tex2DGroup->mTex2dCoords.push_back(texCoord);
+        }
+    }
+}
+
+void XmlSerializer::ReadTextureGroup(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    int id = IdNotSet;
+    if (!XmlParser::getIntAttribute(node, XmlTag::id, id)) {
+        return;
+    }
+
+    Texture2DGroup *group = new Texture2DGroup(id);
+    ReadTextureCoords2D(node, group);
+    mResourcesDictionnary.insert(std::make_pair(id, group));
+}
+
+aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basematerialsId) {
+    aiMaterial *material = new aiMaterial();
+    material->mNumProperties = 0;
+    std::string name;
+    bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name);
+
+    std::string stdMaterialName;
+    const std::string strId(ai_to_string(basematerialsId));
+    stdMaterialName += "id";
+    stdMaterialName += strId;
+    stdMaterialName += "_";
+    if (hasName) {
+        stdMaterialName += std::string(name);
+    } else {
+        stdMaterialName += "basemat_";
+        stdMaterialName += ai_to_string(mMaterials.size());
+    }
+
+    aiString assimpMaterialName(stdMaterialName);
+    material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME);
+
+    assignDiffuseColor(node, material);
+
+    return material;
+}
+
+void XmlSerializer::StoreMaterialsInScene(aiScene *scene) {
+    if (nullptr == scene || mMaterials.empty()) {
+        return;
+    }
+
+    scene->mNumMaterials = static_cast<unsigned int>(mMaterials.size());
+    scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
+    for (size_t i = 0; i < mMaterials.size(); ++i) {
+        scene->mMaterials[i] = mMaterials[i];
+    }
+}
+
+} // namespace D3MF
+} // namespace Assimp

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

@@ -0,0 +1,96 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2021, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+
+#include <assimp/XmlParser.h>
+#include <assimp/mesh.h>
+#include <vector>
+#include <map>
+
+struct aiNode;
+struct aiMesh;
+struct aiMaterial;
+
+namespace Assimp {
+namespace D3MF {
+
+class Resource;
+class D3MFOpcPackage;
+class Object;
+class Texture2DGroup;
+class EmbeddedTexture;
+
+class XmlSerializer {
+public:
+    XmlSerializer(XmlParser *xmlParser);
+    ~XmlSerializer();
+    void ImportXml(aiScene *scene);
+
+private:
+    void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform);
+    void ReadObject(XmlNode &node);
+    aiMesh *ReadMesh(XmlNode &node);
+    void ReadMetadata(XmlNode &node);
+    void ImportVertices(XmlNode &node, aiMesh *mesh);
+    void ImportTriangles(XmlNode &node, aiMesh *mesh);
+    void ReadBaseMaterials(XmlNode &node);
+    void ReadEmbeddecTexture(XmlNode &node);
+    void StoreEmbeddedTexture(EmbeddedTexture *tex);
+    void ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup);
+    void ReadTextureGroup(XmlNode &node);
+    aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId);
+    void StoreMaterialsInScene(aiScene *scene);
+
+private:
+    struct MetaEntry {
+        std::string name;
+        std::string value;
+    };
+    std::vector<MetaEntry> mMetaData;
+    std::vector<EmbeddedTexture *> mEmbeddedTextures;
+    std::vector<aiMaterial *> mMaterials;
+    std::map<unsigned int, Resource *> mResourcesDictionnary;
+    unsigned int mMeshCount;
+    XmlParser *mXmlParser;
+};
+
+} // namespace D3MF
+} // namespace Assimp

+ 1 - 1
code/AssetLib/Assbin/AssbinFileWriter.cpp

@@ -172,7 +172,7 @@ inline size_t Write<aiQuaternion>(IOStream *stream, const aiQuaternion &v) {
     t += Write<float>(stream, v.z);
     ai_assert(t == 16);
 
-    return 16;
+    return t;
 }
 
 // -----------------------------------------------------------------------------------

+ 27 - 15
code/AssetLib/Assjson/json_exporter.cpp

@@ -41,12 +41,17 @@ public:
     enum {
         Flag_DoNotIndent = 0x1,
         Flag_WriteSpecialFloats = 0x2,
+        Flag_SkipWhitespaces = 0x4
     };
-
+    
     JSONWriter(Assimp::IOStream &out, unsigned int flags = 0u) :
-            out(out), first(), flags(flags) {
+            out(out), indent (""), newline("\n"), space(" "), buff (), first(false), flags(flags) {
         // make sure that all formatting happens using the standard, C locale and not the user's current locale
         buff.imbue(std::locale("C"));
+        if (flags & Flag_SkipWhitespaces) {
+            newline = "";
+            space = "";
+        }
     }
 
     ~JSONWriter() {
@@ -70,7 +75,7 @@ public:
     void Key(const std::string &name) {
         AddIndentation();
         Delimit();
-        buff << '\"' + name + "\": ";
+        buff << '\"' + name + "\":" << space;
     }
 
     template <typename Literal>
@@ -78,12 +83,12 @@ public:
         AddIndentation();
         Delimit();
 
-        LiteralToString(buff, name) << '\n';
+        LiteralToString(buff, name) << newline;
     }
 
     template <typename Literal>
     void SimpleValue(const Literal &s) {
-        LiteralToString(buff, s) << '\n';
+        LiteralToString(buff, s) << newline;
     }
 
     void SimpleValue(const void *buffer, size_t len) {
@@ -102,7 +107,7 @@ public:
             }
         }
 
-        buff << '\"' << cur_out << "\"\n";
+        buff << '\"' << cur_out << "\"" << newline;
         delete[] cur_out;
     }
 
@@ -115,7 +120,7 @@ public:
             }
         }
         first = true;
-        buff << "{\n";
+        buff << "{" << newline;
         PushIndent();
     }
 
@@ -123,7 +128,7 @@ public:
         PopIndent();
         AddIndentation();
         first = false;
-        buff << "}\n";
+        buff << "}" << newline;
     }
 
     void StartArray(bool is_element = false) {
@@ -135,19 +140,19 @@ public:
             }
         }
         first = true;
-        buff << "[\n";
+        buff << "[" << newline;
         PushIndent();
     }
 
     void EndArray() {
         PopIndent();
         AddIndentation();
-        buff << "]\n";
+        buff << "]" << newline;
         first = false;
     }
 
     void AddIndentation() {
-        if (!(flags & Flag_DoNotIndent)) {
+        if (!(flags & Flag_DoNotIndent) && !(flags & Flag_SkipWhitespaces)) {
             buff << indent;
         }
     }
@@ -156,7 +161,7 @@ public:
         if (!first) {
             buff << ',';
         } else {
-            buff << ' ';
+            buff << space;
             first = false;
         }
     }
@@ -227,7 +232,9 @@ private:
 
 private:
     Assimp::IOStream &out;
-    std::string indent, newline;
+    std::string indent;
+    std::string newline;
+    std::string space;
     std::stringstream buff;
     bool first;
 
@@ -765,7 +772,7 @@ void Write(JSONWriter &out, const aiScene &ai) {
     out.EndObj();
 }
 
-void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *scene, const Assimp::ExportProperties *) {
+void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *scene, const Assimp::ExportProperties *pProperties) {
     std::unique_ptr<Assimp::IOStream> str(io->Open(file, "wt"));
     if (!str) {
         throw DeadlyExportError("could not open output file");
@@ -782,7 +789,12 @@ void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *sc
         splitter.Execute(scenecopy_tmp);
 
         // XXX Flag_WriteSpecialFloats is turned on by default, right now we don't have a configuration interface for exporters
-        JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats);
+
+        unsigned int flags = JSONWriter::Flag_WriteSpecialFloats;
+        if (pProperties->GetPropertyBool("JSON_SKIP_WHITESPACES", false)) {
+            flags |= JSONWriter::Flag_SkipWhitespaces;
+        }
+        JSONWriter s(*str, flags);
         Write(s, *scenecopy_tmp);
 
     } catch (...) {

+ 10 - 9
code/AssetLib/Collada/ColladaLoader.cpp

@@ -135,14 +135,15 @@ bool ColladaLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool
 
     // XML - too generic, we need to open the file and search for typical keywords
     if (extension == "xml" || !extension.length() || checkSig) {
-        /*  If CanRead() is called in order to check whether we
-         *  support a specific file extension in general pIOHandler
-         *  might be nullptr and it's our duty to return true here.
-         */
-        if (!pIOHandler) {
+        //  If CanRead() is called in order to check whether we
+        //  support a specific file extension in general pIOHandler
+        //  might be nullptr and it's our duty to return true here.
+        if (nullptr == pIOHandler) {
             return true;
         }
-        static const char *tokens[] = { "<collada" };
+        static const char* tokens[] = {
+            "<collada"
+        };
         return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
     }
 
@@ -573,7 +574,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
 
     // now place all mesh references we gathered in the target node
     pTarget->mNumMeshes = static_cast<unsigned int>(newMeshRefs.size());
-    if (newMeshRefs.size()) {
+    if (!newMeshRefs.empty()) {
         struct UIntTypeConverter {
             unsigned int operator()(const size_t &v) const {
                 return static_cast<unsigned int>(v);
@@ -1544,7 +1545,7 @@ void ColladaLoader::AddTexture(aiMaterial &mat,
         map = -1;
         for (std::string::const_iterator it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) {
             if (IsNumeric(*it)) {
-                map = strtoul10(&(*it));
+                map = strtoul10(&(*it));        
                 break;
             }
         }
@@ -1686,7 +1687,7 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/)
 
         // store the material
         mMaterialIndexByName[matIt->first] = newMats.size();
-        newMats.push_back(std::pair<Effect *, aiMaterial *>(&effect, mat));
+        newMats.emplace_back(&effect, mat);
     }
     // ScenePreprocessor generates a default material automatically if none is there.
     // All further code here in this loader works well without a valid material so

+ 3 - 3
code/AssetLib/Collada/ColladaParser.cpp

@@ -914,7 +914,7 @@ void ColladaParser::ReadMaterial(XmlNode &node, Collada::Material &pMaterial) {
         if (currentName == "instance_effect") {
             std::string url;
             readUrlAttribute(currentNode, url);
-            pMaterial.mEffect = url.c_str();
+            pMaterial.mEffect = url;
         }
     }
 }
@@ -2204,8 +2204,8 @@ void ColladaParser::ReadMaterialVertexInputBinding(XmlNode &node, Collada::Seman
 
 void ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive) {
     // Attempt to load any undefined Collada::Image in ImageLibrary
-    for (ImageLibrary::iterator it = mImageLibrary.begin(); it != mImageLibrary.end(); ++it) {
-        Collada::Image &image = (*it).second;
+    for (auto & it : mImageLibrary) {
+        Collada::Image &image = it.second;
 
         if (image.mImageData.empty()) {
             std::unique_ptr<IOStream> image_file(zip_archive.Open(image.mFileName.c_str()));

+ 5 - 3
code/AssetLib/FBX/FBXConverter.cpp

@@ -917,8 +917,10 @@ void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root
         } else if (line) {
             const std::vector<unsigned int> &indices = ConvertLine(*line, root_node);
             std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
-        } else {
+        } else if (geo) {
             FBXImporter::LogWarn("ignoring unrecognized geometry: ", geo->Name());
+        } else {
+            FBXImporter::LogWarn("skipping null geometry");
         }
     }
 
@@ -2131,7 +2133,7 @@ void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const Propert
     if (ok) {
         out_mat->AddProperty(&Emissive, 1, AI_MATKEY_COLOR_EMISSIVE);
     } else {
-        const aiColor3D &emissiveColor = GetColorPropertyFromMaterial(props, "Maya|emissive", ok);
+        const aiColor3D &emissiveColor = GetColorProperty(props, "Maya|emissive", ok);
         if (ok) {
             out_mat->AddProperty(&emissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE);
         }
@@ -2218,7 +2220,7 @@ void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const Propert
     }
 
     // PBR material information
-    const aiColor3D &baseColor = GetColorPropertyFromMaterial(props, "Maya|base_color", ok);
+    const aiColor3D &baseColor = GetColorProperty(props, "Maya|base_color", ok);
     if (ok) {
         out_mat->AddProperty(&baseColor, 1, AI_MATKEY_BASE_COLOR);
     }

+ 12 - 16
code/AssetLib/FBX/FBXParser.cpp

@@ -192,6 +192,10 @@ Scope::Scope(Parser& parser,bool topLevel)
         }
 
         const std::string& str = n->StringContents();
+        if (str.empty()) {
+            ParseError("unexpected content: empty string.");
+        }
+        
         elements.insert(ElementMap::value_type(str,new_Element(*n,parser)));
 
         // Element() should stop at the next Key token (or right after a Close token)
@@ -642,8 +646,7 @@ void ParseVectorDataArray(std::vector<aiVector3D>& out, const Element& el)
 
         ai_assert(data == end);
         uint64_t dataToRead = static_cast<uint64_t>(count) * (type == 'd' ? 8 : 4);
-        ai_assert(buff.size() == dataToRead);
-        if (dataToRead > buff.size()) {
+        if (dataToRead != buff.size()) {
             ParseError("Invalid read size (binary)",&el);
         }
 
@@ -733,8 +736,7 @@ void ParseVectorDataArray(std::vector<aiColor4D>& out, const Element& el)
 
         ai_assert(data == end);
         uint64_t dataToRead = static_cast<uint64_t>(count) * (type == 'd' ? 8 : 4);
-        ai_assert(buff.size() == dataToRead);
-        if (dataToRead > buff.size()) {
+        if (dataToRead != buff.size()) {
             ParseError("Invalid read size (binary)",&el);
         }
 
@@ -816,8 +818,7 @@ void ParseVectorDataArray(std::vector<aiVector2D>& out, const Element& el)
 
         ai_assert(data == end);
         uint64_t dataToRead = static_cast<uint64_t>(count) * (type == 'd' ? 8 : 4);
-        ai_assert(buff.size() == dataToRead);
-        if (dataToRead > buff.size()) {
+        if (dataToRead != buff.size()) {
             ParseError("Invalid read size (binary)",&el);
         }
 
@@ -892,8 +893,7 @@ void ParseVectorDataArray(std::vector<int>& out, const Element& el)
 
         ai_assert(data == end);
         uint64_t dataToRead = static_cast<uint64_t>(count) * 4;
-        ai_assert(buff.size() == dataToRead);
-        if (dataToRead > buff.size()) {
+        if (dataToRead != buff.size()) {
             ParseError("Invalid read size (binary)",&el);
         }
 
@@ -954,8 +954,7 @@ void ParseVectorDataArray(std::vector<float>& out, const Element& el)
 
         ai_assert(data == end);
         uint64_t dataToRead = static_cast<uint64_t>(count) * (type == 'd' ? 8 : 4);
-        ai_assert(buff.size() == dataToRead);
-        if (dataToRead > buff.size()) {
+        if (dataToRead != buff.size()) {
             ParseError("Invalid read size (binary)",&el);
         }
 
@@ -1019,8 +1018,7 @@ void ParseVectorDataArray(std::vector<unsigned int>& out, const Element& el)
 
         ai_assert(data == end);
         uint64_t dataToRead = static_cast<uint64_t>(count) * 4;
-        ai_assert(buff.size() == dataToRead);
-        if (dataToRead > buff.size()) {
+        if (dataToRead != buff.size()) {
             ParseError("Invalid read size (binary)",&el);
         }
 
@@ -1088,8 +1086,7 @@ void ParseVectorDataArray(std::vector<uint64_t>& out, const Element& el)
 
         ai_assert(data == end);
         uint64_t dataToRead = static_cast<uint64_t>(count) * 8;
-        ai_assert(buff.size() == dataToRead);
-        if (dataToRead > buff.size()) {
+        if (dataToRead != buff.size()) {
             ParseError("Invalid read size (binary)",&el);
         }
 
@@ -1150,8 +1147,7 @@ void ParseVectorDataArray(std::vector<int64_t>& out, const Element& el)
 
         ai_assert(data == end);
         uint64_t dataToRead = static_cast<uint64_t>(count) * 8;
-        ai_assert(buff.size() == dataToRead);
-        if (dataToRead > buff.size()) {
+        if (dataToRead != buff.size()) {
             ParseError("Invalid read size (binary)",&el);
         }
 

+ 7 - 7
code/AssetLib/M3D/M3DImporter.cpp

@@ -233,12 +233,12 @@ void M3DImporter::importMaterials(const M3DWrapper &m3d) {
     ASSIMP_LOG_DEBUG("M3D: importMaterials ", mScene->mNumMaterials);
 
     // add a default material as first
-    aiMaterial *mat = new aiMaterial;
-    mat->AddProperty(&name, AI_MATKEY_NAME);
+    aiMaterial *defaultMat = new aiMaterial;
+    defaultMat->AddProperty(&name, AI_MATKEY_NAME);
     c.a = 1.0f;
     c.b = c.g = c.r = 0.6f;
-    mat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE);
-    mScene->mMaterials[0] = mat;
+    defaultMat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE);
+    mScene->mMaterials[0] = defaultMat;
 
     if (!m3d->nummaterial || !m3d->material) {
         return;
@@ -300,12 +300,12 @@ void M3DImporter::importMaterials(const M3DWrapper &m3d) {
                     m->prop[j].value.textureid < m3d->numtexture &&
                     m3d->texture[m->prop[j].value.textureid].name) {
                 name.Set(std::string(std::string(m3d->texture[m->prop[j].value.textureid].name) + ".png"));
-                mat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index);
+                newMat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index);
                 n = 0;
-                mat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index);
+                newMat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index);
             }
         }
-        mScene->mMaterials[i + 1] = mat;
+        mScene->mMaterials[i + 1] = newMat;
     }
 }
 

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

@@ -162,7 +162,7 @@ void ObjFileImporter::InternReadFile(const std::string &file, aiScene *pScene, I
 // ------------------------------------------------------------------------------------------------
 //  Create the data from parsed obj-file
 void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene *pScene) {
-    if (0L == pModel) {
+    if (nullptr == pModel) {
         return;
     }
 
@@ -468,7 +468,7 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model *pModel,
             }
 
             // Copy all vertex colors
-            if (!pModel->m_VertexColors.empty()) {
+            if (vertex < pModel->m_VertexColors.size()) {
                 const aiVector3D &color = pModel->m_VertexColors[vertex];
                 pMesh->mColors[0][newIndex] = aiColor4D(color.x, color.y, color.z, 1.0);
             }

+ 2 - 5
code/AssetLib/XGL/XGLLoader.cpp

@@ -250,7 +250,7 @@ void XGLImporter::ReadWorld(XmlNode &node, TempScope &scope) {
 		}
 	}
 
-	aiNode *const nd = ReadObject(node, scope, true);
+	aiNode *const nd = ReadObject(node, scope);
 	if (!nd) {
 		ThrowException("failure reading <world>");
 	}
@@ -296,16 +296,13 @@ aiLight *XGLImporter::ReadDirectionalLight(XmlNode &node) {
 }
 
 // ------------------------------------------------------------------------------------------------
-aiNode *XGLImporter::ReadObject(XmlNode &node, TempScope &scope, bool skipFirst/*, const char *closetag */) {
+aiNode *XGLImporter::ReadObject(XmlNode &node, TempScope &scope) {
 	aiNode *nd = new aiNode;
 	std::vector<aiNode *> children;
 	std::vector<unsigned int> meshes;
 
 	try {
 		for (XmlNode &child : node.children()) {
-
-			skipFirst = false;
-
 			const std::string &s = ai_stdStrToLower(child.name());
 			if (s == "mesh") {
 				const size_t prev = scope.meshes_linear.size();

+ 1 - 1
code/AssetLib/XGL/XGLLoader.h

@@ -185,7 +185,7 @@ private:
     void ReadWorld(XmlNode &node, TempScope &scope);
     void ReadLighting(XmlNode &node, TempScope &scope);
     aiLight *ReadDirectionalLight(XmlNode &node);
-    aiNode *ReadObject(XmlNode &node, TempScope &scope, bool skipFirst = false/*, const char *closetag = "object"*/);
+    aiNode *ReadObject(XmlNode &node, TempScope &scope);
     bool ReadMesh(XmlNode &node, TempScope &scope);
     void ReadMaterial(XmlNode &node, TempScope &scope);
     aiVector2D ReadVec2(XmlNode &node);

+ 1 - 0
code/AssetLib/glTF/glTFExporter.cpp

@@ -526,6 +526,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref<Mesh>& meshRef, Ref<Buf
 
 #if defined(__has_warning)
 #if __has_warning("-Wunused-but-set-variable")
+#pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #endif
 #endif

+ 1 - 1
code/AssetLib/glTF2/glTF2Asset.inl

@@ -1522,7 +1522,7 @@ inline bool GetAttribTargetVector(Mesh::Primitive &p, const int targetIndex, con
 
 inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
     Value *curName = FindMember(pJSON_Object, "name");
-    if (nullptr != curName) {
+    if (nullptr != curName && curName->IsString()) {
         name = curName->GetString();
     }
 

+ 20 - 2
code/AssetLib/glTF2/glTF2Importer.cpp

@@ -1336,6 +1336,23 @@ std::unordered_map<unsigned int, AnimationSamplers> GatherSamplers(Animation &an
             continue;
         }
 
+        auto& animsampler = anim.samplers[channel.sampler];
+
+        if (!animsampler.input) {
+            ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping.");
+            continue;
+        }
+
+        if (!animsampler.output) {
+            ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler output. Skipping.");
+            continue;
+        }
+
+        if (animsampler.input->count > animsampler.output->count) {
+            ASSIMP_LOG_WARN("Animation ", anim.name, ": Number of keyframes in sampler input ", animsampler.input->count, " exceeds number of keyframes in sampler output ", animsampler.output->count);
+            continue;
+        }
+
         const unsigned int node_index = channel.target.node.GetIndex();
 
         AnimationSamplers &sampler = samplers[node_index];
@@ -1470,10 +1487,11 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
         }
     }
 
-    if (numEmbeddedTexs == 0)
+    if (numEmbeddedTexs == 0) {
         return;
+    }
 
-      ASSIMP_LOG_DEBUG("Importing ", numEmbeddedTexs, " embedded textures");
+    ASSIMP_LOG_DEBUG("Importing ", numEmbeddedTexs, " embedded textures");
 
     mScene->mTextures = new aiTexture *[numEmbeddedTexs];
     std::fill(mScene->mTextures, mScene->mTextures + numEmbeddedTexs, nullptr);

+ 4 - 1
code/CMakeLists.txt

@@ -820,7 +820,10 @@ ADD_ASSIMP_IMPORTER( GLTF
   AssetLib/glTF2/glTF2Importer.h
 )
 
-ADD_ASSIMP_IMPORTER( 3MF
+ADD_ASSIMP_IMPORTER(3MF
+  AssetLib/3MF/3MFTypes.h
+  AssetLib/3MF/XmlSerializer.h
+  AssetLib/3MF/XmlSerializer.cpp
   AssetLib/3MF/D3MFImporter.h
   AssetLib/3MF/D3MFImporter.cpp
   AssetLib/3MF/D3MFOpcPackage.h

+ 13 - 0
code/Common/Assimp.cpp

@@ -72,12 +72,25 @@ namespace Assimp {
 // underlying structure for aiPropertyStore
 typedef BatchLoader::PropertyMap PropertyMap;
 
+#if defined(__has_warning)
+#if __has_warning("-Wordered-compare-function-pointers")
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wordered-compare-function-pointers"
+#endif
+#endif
+
 /** Stores the LogStream objects for all active C log streams */
 struct mpred {
     bool operator()(const aiLogStream &s0, const aiLogStream &s1) const {
         return s0.callback < s1.callback && s0.user < s1.user;
     }
 };
+
+#if defined(__has_warning)
+#if __has_warning("-Wordered-compare-function-pointers")
+#pragma GCC diagnostic pop
+#endif
+#endif
 typedef std::map<aiLogStream, Assimp::LogStream *, mpred> LogStreamMap;
 
 /** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */

+ 1 - 1
code/Common/DefaultIOSystem.cpp

@@ -173,7 +173,7 @@ inline static std::string MakeAbsolutePath(const char *in) {
         free(ret);
     }
 #endif
-    if (!ret) {
+    else {
         // preserve the input path, maybe someone else is able to fix
         // the path before it is accessed (e.g. our file system filter)
         ASSIMP_LOG_WARN("Invalid path: ", std::string(in));

+ 5 - 2
code/Common/RemoveComments.cpp

@@ -59,13 +59,16 @@ void CommentRemover::RemoveLineComments(const char* szComment,
     ai_assert(nullptr != szBuffer);
     ai_assert(*szComment);
 
-    const size_t len = strlen(szComment);
+    size_t len = strlen(szComment);
+    const size_t lenBuffer = strlen(szBuffer);
+    if (len > lenBuffer) {
+        len = lenBuffer;
+    }
     while (*szBuffer)   {
 
         // skip over quotes
         if (*szBuffer == '\"' || *szBuffer == '\'')
             while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\'');
-
         if (!strncmp(szBuffer,szComment,len)) {
             while (!IsLineEnd(*szBuffer))
                 *szBuffer++ = chReplacement;

+ 3 - 0
code/Common/ScenePreprocessor.cpp

@@ -89,6 +89,9 @@ void ScenePreprocessor::ProcessScene() {
         ASSIMP_LOG_DEBUG("ScenePreprocessor: Adding default material \'" AI_DEFAULT_MATERIAL_NAME "\'");
 
         for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
+            if (nullptr == scene->mMeshes[i]) {
+                continue;
+            }
             scene->mMeshes[i]->mMaterialIndex = scene->mNumMaterials;
         }
 

+ 9 - 3
code/Material/MaterialSystem.cpp

@@ -555,17 +555,23 @@ uint32_t Assimp::ComputeMaterialHash(const aiMaterial *mat, bool includeMatName
 }
 
 // ------------------------------------------------------------------------------------------------
-void aiMaterial::CopyPropertyList(aiMaterial *pcDest,
+void aiMaterial::CopyPropertyList(aiMaterial *const pcDest,
         const aiMaterial *pcSrc) {
     ai_assert(nullptr != pcDest);
     ai_assert(nullptr != pcSrc);
+    ai_assert(pcDest->mNumProperties <= pcDest->mNumAllocated);
+    ai_assert(pcSrc->mNumProperties <= pcSrc->mNumAllocated);
 
-    unsigned int iOldNum = pcDest->mNumProperties;
+    const unsigned int iOldNum = pcDest->mNumProperties;
     pcDest->mNumAllocated += pcSrc->mNumAllocated;
     pcDest->mNumProperties += pcSrc->mNumProperties;
 
+    const unsigned int numAllocated = pcDest->mNumAllocated;
     aiMaterialProperty **pcOld = pcDest->mProperties;
-    pcDest->mProperties = new aiMaterialProperty *[pcDest->mNumAllocated];
+    pcDest->mProperties = new aiMaterialProperty *[numAllocated];
+
+    ai_assert(!iOldNum || pcOld);
+    ai_assert(iOldNum < numAllocated);
 
     if (iOldNum && pcOld) {
         for (unsigned int i = 0; i < iOldNum; ++i) {

+ 1 - 1
code/PostProcessing/CalcTangentsProcess.cpp

@@ -56,7 +56,7 @@ using namespace Assimp;
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 CalcTangentsProcess::CalcTangentsProcess() :
-        configMaxAngle(AI_DEG_TO_RAD(45.f)), configSourceUV(0) {
+        configMaxAngle(float(AI_DEG_TO_RAD(45.f))), configSourceUV(0) {
     // nothing to do here
 }
 

+ 1 - 1
code/PostProcessing/OptimizeGraph.cpp

@@ -170,7 +170,7 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode *nd, std::list<aiNode *> &n
 			++it;
 		}
 		if (join_master && !join.empty()) {
-			join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%i", count_merged++);
+			join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%u", count_merged++);
 
 			unsigned int out_meshes = 0;
 			for (std::list<aiNode *>::const_iterator it = join.cbegin(); it != join.cend(); ++it) {

+ 1 - 1
code/PostProcessing/PretransformVertices.cpp

@@ -481,7 +481,7 @@ void PretransformVertices::Execute(aiScene *pScene) {
 			pScene->mMeshes[i]->mNumBones = 0;
 		}
 	} else {
-		apcOutMeshes.reserve(pScene->mNumMaterials << 1u);
+		apcOutMeshes.reserve(static_cast<size_t>(pScene->mNumMaterials) << 1u);
 		std::list<unsigned int> aiVFormats;
 
 		std::vector<unsigned int> s(pScene->mNumMeshes, 0);

+ 1 - 1
code/PostProcessing/SortByPTypeProcess.cpp

@@ -127,7 +127,7 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
     unsigned int aiNumMeshesPerPType[4] = { 0, 0, 0, 0 };
 
     std::vector<aiMesh *> outMeshes;
-    outMeshes.reserve(pScene->mNumMeshes << 1u);
+    outMeshes.reserve(static_cast<size_t>(pScene->mNumMeshes) << 1u);
 
     bool bAnyChanges = false;
 

+ 1 - 2
contrib/openddlparser/code/OpenDDLExport.cpp

@@ -134,10 +134,9 @@ bool OpenDDLExport::writeToStream(const std::string &statement) {
 }
 
 bool OpenDDLExport::writeNode(DDLNode *node, std::string &statement) {
-    bool success(true);
     writeNodeHeader(node, statement);
     if (node->hasProperties()) {
-        success |= writeProperties(node, statement);
+        writeProperties(node, statement);
     }
     writeLineEnd(statement);
 

+ 4 - 4
include/assimp/Logger.hpp

@@ -99,8 +99,8 @@ public:
     virtual ~Logger();
 
     // ----------------------------------------------------------------------
-    /** @brief  Writes a info message
-     *  @param  message Info message*/
+    /** @brief  Writes a debug message
+     *  @param  message Debug message*/
     void debug(const char* message);
 
     template<typename... T>
@@ -109,7 +109,7 @@ public:
     }
 
     // ----------------------------------------------------------------------
-	/** @brief  Writes a debug message
+    /** @brief  Writes a debug message
      *   @param message Debug message*/
     void verboseDebug(const char* message);
 
@@ -140,7 +140,7 @@ public:
 
     // ----------------------------------------------------------------------
     /** @brief  Writes an error message
-     *  @param  message Info message*/
+     *  @param  message Error message*/
     void error(const char* message);
 
     template<typename... T>

+ 1 - 1
include/assimp/scene.h

@@ -400,7 +400,7 @@ struct aiScene
 
     //! Returns an embedded texture and its index
     std::pair<const aiTexture*, int> GetEmbeddedTextureAndIndex(const char* filename) const {
-        if(nullptr==filename) {
+        if (nullptr==filename) {
             return std::make_pair(nullptr, -1);
         }
         // lookup using texture ID (if referenced like: "*1", "*2", etc.)

+ 27 - 6
include/assimp/vector3.h

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2021, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -65,27 +63,49 @@ template<typename TReal> class aiMatrix3x3t;
 template<typename TReal> class aiMatrix4x4t;
 
 // ---------------------------------------------------------------------------
-/** Represents a three-dimensional vector. */
+/// @brief  Represents a three-dimensional vector.
+// ---------------------------------------------------------------------------
 template <typename TReal>
 class aiVector3t {
 public:
+    /// @brief  The default class constructor.
     aiVector3t() AI_NO_EXCEPT : x(), y(), z() {}
+    
+    /// @brief  The class constructor with the components.
+    /// @param  _x  The x-component for the vector.
+    /// @param  _y  The y-component for the vector.
+    /// @param  _z  The z-component for the vector.
     aiVector3t(TReal _x, TReal _y, TReal _z) : x(_x), y(_y), z(_z) {}
+    
+    /// @brief  The class constructor with a default value.
+    /// @param  _xyz  The value for x, y and z.
     explicit aiVector3t (TReal _xyz ) : x(_xyz), y(_xyz), z(_xyz) {}
+    
+    /// @brief  The copy constructor.
+    /// @param  o The instance to copy from.
     aiVector3t( const aiVector3t& o ) = default;
 
-    // combined operators
+    /// @brief  combined operators
+    /// @brief  The copy constructor.
     const aiVector3t& operator += (const aiVector3t& o);
+
+    /// @brief  The copy constructor.
     const aiVector3t& operator -= (const aiVector3t& o);
+
+    /// @brief  The copy constructor.
     const aiVector3t& operator *= (TReal f);
+
+    /// @brief  The copy constructor.
     const aiVector3t& operator /= (TReal f);
 
-    // transform vector by matrix
+    /// @brief  Transform vector by matrix
     aiVector3t& operator *= (const aiMatrix3x3t<TReal>& mat);
     aiVector3t& operator *= (const aiMatrix4x4t<TReal>& mat);
 
-    // access a single element
+    /// @brief  access a single element, const.
     TReal operator[](unsigned int i) const;
+
+    /// @brief  access a single element, non-const.
     TReal& operator[](unsigned int i);
 
     // comparison
@@ -93,6 +113,7 @@ public:
     bool operator!= (const aiVector3t& other) const;
     bool operator < (const aiVector3t& other) const;
 
+    /// @brief  
     bool Equal(const aiVector3t& other, TReal epsilon = 1e-6) const;
 
     template <typename TOther>

+ 1 - 1
samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp

@@ -23,7 +23,7 @@
 #endif // _MSC_VER
 
 #define STB_IMAGE_IMPLEMENTATION
-#include "contrib/stb_image/stb_image.h"
+#include "contrib/stb/stb_image.h"
 
 #ifdef _MSC_VER
 #pragma warning(default: 4100) // Enable warning 'unreferenced formal parameter'

+ 12 - 1
test/unit/ImportExport/utAssjsonImportExport.cpp

@@ -58,7 +58,18 @@ public:
 
         Exporter exporter;
         aiReturn res = exporter.Export(scene, "assjson", "./spider_test.json");
-        return aiReturn_SUCCESS == res;
+        if (aiReturn_SUCCESS != res) {
+            return false;
+        }
+
+        Assimp::ExportProperties exportProperties;
+        exportProperties.SetPropertyBool("JSON_SKIP_WHITESPACES", true);
+        aiReturn resNoWhitespace = exporter.Export(scene, "assjson", "./spider_test_nowhitespace.json", 0u, &exportProperties);
+        if (aiReturn_SUCCESS != resNoWhitespace) {
+            return false;
+        }
+
+        return true;
     }
 };
 

+ 6 - 5
test/unit/utglTF2ImportExport.cpp

@@ -615,12 +615,12 @@ TEST_F(utglTF2ImportExport, texcoords) {
     aiTextureMapMode modes[2];
     EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes));
     EXPECT_STREQ(path.C_Str(), "texture.png");
-    EXPECT_EQ(uvIndex, 0);
+    EXPECT_EQ(uvIndex, 0u);
 
     uvIndex = 255;
     EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes));
     EXPECT_STREQ(path.C_Str(), "texture.png");
-    EXPECT_EQ(uvIndex, 1);
+    EXPECT_EQ(uvIndex, 1u);
 }
 
 #ifndef ASSIMP_BUILD_NO_EXPORT
@@ -646,12 +646,12 @@ TEST_F(utglTF2ImportExport, texcoords_export) {
     aiTextureMapMode modes[2];
     EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes));
     EXPECT_STREQ(path.C_Str(), "texture.png");
-    EXPECT_EQ(uvIndex, 0);
+    EXPECT_EQ(uvIndex, 0u);
 
     uvIndex = 255;
     EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes));
     EXPECT_STREQ(path.C_Str(), "texture.png");
-    EXPECT_EQ(uvIndex, 1);
+    EXPECT_EQ(uvIndex, 1u);
 }
 
 #endif // ASSIMP_BUILD_NO_EXPORT
@@ -748,7 +748,8 @@ TEST_F(utglTF2ImportExport, import_dracoEncoded) {
 
 TEST_F(utglTF2ImportExport, wrongTypes) {
     // Deliberately broken version of the BoxTextured.gltf asset.
-    std::vector<std::tuple<std::string, std::string, std::string, std::string>> wrongTypes = {
+    using tup_T = std::tuple<std::string, std::string, std::string, std::string>;
+    std::vector<tup_T> wrongTypes = {
         { "/glTF2/wrongTypes/badArray.gltf", "array", "primitives", "meshes[0]" },
         { "/glTF2/wrongTypes/badString.gltf", "string", "name", "scenes[0]" },
         { "/glTF2/wrongTypes/badUint.gltf", "uint", "index", "materials[0]" },

+ 4 - 3
tools/assimp_view/Material.cpp

@@ -325,9 +325,10 @@ int CMaterialManager::FindValidPath(aiString* p_szString)
 
     // first check whether we can directly load the file
     FILE* pFile = fopen(p_szString->data,"rb");
-    if (pFile)fclose(pFile);
-    else
-    {
+    if (pFile) {
+        fclose(pFile);
+    }
+    else {
         // check whether we can use the directory of  the asset as relative base
         char szTemp[MAX_PATH*2], tmp2[MAX_PATH*2];
         strcpy(szTemp, g_szFileName);

+ 35 - 38
tools/assimp_view/MaterialManager.h

@@ -52,24 +52,11 @@ namespace AssimpView {
     */
 //-------------------------------------------------------------------------------
 class CMaterialManager {
-private:
     friend class CDisplay;
 
-    // default constructor
-    CMaterialManager() :
-            m_iShaderCount(0), sDefaultTexture() {}
-
-    ~CMaterialManager() {
-        if (sDefaultTexture) {
-            sDefaultTexture->Release();
-        }
-        Reset();
-    }
-
 public:
     //------------------------------------------------------------------
     // Singleton accessors
-    static CMaterialManager s_cInstance;
     inline static CMaterialManager &Instance() {
         return s_cInstance;
     }
@@ -80,24 +67,20 @@ public:
     // Must be called before CreateMaterial() to prevent memory leaking
     void DeleteMaterial(AssetHelper::MeshHelper *pcIn);
 
-    //------------------------------------------------------------------
-    // Create the material for a mesh.
-    //
-    // The function checks whether an identical shader is already in use.
-    // A shader is considered to be identical if it has the same input
-    // signature and takes the same number of texture channels.
-    int CreateMaterial(AssetHelper::MeshHelper *pcMesh,
-            const aiMesh *pcSource);
-
-    //------------------------------------------------------------------
-    // Setup the material for a given mesh
-    // pcMesh Mesh to be rendered
-    // pcProj Projection matrix
-    // aiMe Current world matrix
-    // pcCam Camera matrix
-    // vPos Position of the camera
-    // TODO: Extract camera position from matrix ...
-    //
+    /// @brief  Create the material for a mesh.
+    ///
+    /// The function checks whether an identical shader is already in use.
+    /// A shader is considered to be identical if it has the same input
+    /// signature and takes the same number of texture channels.
+    int CreateMaterial(AssetHelper::MeshHelper *pcMesh, const aiMesh *pcSource);
+    
+    ///	@brief  Setup the material for a given mesh.
+    /// @param  pcMesh   Mesh to be rendered
+    /// @param  pcProj   Projection matrix
+    /// @param  aiMe     Current world matrix
+    /// @param  pcCam    Camera matrix
+    /// @param  vPos     Position of the camera
+    /// @return 0 if successful.
     int SetupMaterial(AssetHelper::MeshHelper *pcMesh,
             const aiMatrix4x4 &pcProj,
             const aiMatrix4x4 &aiMe,
@@ -143,14 +126,29 @@ public:
     // Reset the state of the class
     // Called whenever a new asset is loaded
     inline void Reset() {
-        this->m_iShaderCount = 0;
-        for (TextureCache::iterator it = sCachedTextures.begin(); it != sCachedTextures.end(); ++it) {
-            (*it).second->Release();
+        m_iShaderCount = 0;
+        for (auto & sCachedTexture : sCachedTextures) {
+            sCachedTexture.second->Release();
         }
         sCachedTextures.clear();
     }
 
 private:
+    // The default constructor
+    CMaterialManager() :
+            m_iShaderCount(0),
+            sDefaultTexture() {
+        // empty
+    }
+
+    // Destructor, private.
+    ~CMaterialManager() {
+        if (sDefaultTexture) {
+            sDefaultTexture->Release();
+        }
+        Reset();
+    }
+
     //------------------------------------------------------------------
     // find a valid path to a texture file
     //
@@ -183,15 +181,14 @@ private:
     bool HasAlphaPixels(IDirect3DTexture9 *piTexture);
 
 private:
-    //
+    static CMaterialManager s_cInstance;
+
     // Specifies the number of different shaders generated for
     // the current asset. This number is incremented by CreateMaterial()
     // each time a shader isn't found in cache and needs to be created
-    //
     unsigned int m_iShaderCount;
     IDirect3DTexture9 *sDefaultTexture;
-
-    typedef std::map<std::string, IDirect3DTexture9 *> TextureCache;
+    using TextureCache = std::map<std::string, IDirect3DTexture9 *>;
     TextureCache sCachedTextures;
 };
 

+ 6 - 3
tools/assimp_view/MeshRenderer.cpp

@@ -61,11 +61,14 @@ int CMeshRenderer::DrawUnsorted(unsigned int iIndex) {
     D3DPRIMITIVETYPE type = D3DPT_POINTLIST;
     switch (g_pcAsset->pcScene->mMeshes[iIndex]->mPrimitiveTypes) {
         case aiPrimitiveType_POINT:
-            type = D3DPT_POINTLIST;break;
+            type = D3DPT_POINTLIST;
+            break;
         case aiPrimitiveType_LINE:
-            type = D3DPT_LINELIST;break;
+            type = D3DPT_LINELIST;
+            break;
         case aiPrimitiveType_TRIANGLE:
-            type = D3DPT_TRIANGLELIST;break;
+            type = D3DPT_TRIANGLELIST;
+            break;
     }
     // and draw the mesh
     g_piDevice->DrawIndexedPrimitive(type,