Browse Source

Merge remote-tracking branch 'origin/master' into HEAD

Rahul Sheth 4 years ago
parent
commit
0a5e49252d
78 changed files with 2740 additions and 696 deletions
  1. 2 0
      CMakeLists.txt
  2. 38 38
      code/AssetLib/3DS/3DSHelper.h
  3. 4 4
      code/AssetLib/ASE/ASEParser.h
  4. 1 1
      code/AssetLib/B3D/B3DImporter.cpp
  5. 1 1
      code/AssetLib/B3D/B3DImporter.h
  6. 1 1
      code/AssetLib/Blender/BlenderLoader.cpp
  7. 1 1
      code/AssetLib/COB/COBLoader.cpp
  8. 1 1
      code/AssetLib/Collada/ColladaParser.cpp
  9. 0 2
      code/AssetLib/DXF/DXFLoader.cpp
  10. 4 1
      code/AssetLib/FBX/FBXConverter.cpp
  11. 4 5
      code/AssetLib/FBX/FBXDocument.cpp
  12. 5 0
      code/AssetLib/FBX/FBXDocument.h
  13. 2 3
      code/AssetLib/FBX/FBXExportNode.cpp
  14. 2 3
      code/AssetLib/FBX/FBXExportNode.h
  15. 104 29
      code/AssetLib/FBX/FBXExporter.cpp
  16. 10 11
      code/AssetLib/FBX/FBXExporter.h
  17. 5 0
      code/AssetLib/FBX/FBXMaterial.cpp
  18. 4 5
      code/AssetLib/FBX/FBXProperties.cpp
  19. 1 1
      code/AssetLib/IFC/IFCBoolean.cpp
  20. 1 1
      code/AssetLib/IFC/IFCGeometry.cpp
  21. 3 3
      code/AssetLib/IFC/IFCMaterial.cpp
  22. 6 8
      code/AssetLib/IFC/IFCUtil.h
  23. 1 1
      code/AssetLib/LWS/LWSLoader.cpp
  24. 1 1
      code/AssetLib/M3D/M3DImporter.cpp
  25. 2 2
      code/AssetLib/M3D/M3DImporter.h
  26. 2 2
      code/AssetLib/Ogre/OgreMaterial.cpp
  27. 2 2
      code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp
  28. 3 2
      code/AssetLib/STEPParser/STEPFileReader.cpp
  29. 2 2
      code/AssetLib/Step/STEPFile.h
  30. 2 2
      code/AssetLib/X/XFileExporter.cpp
  31. 3 3
      code/AssetLib/X3D/X3DExporter.hpp
  32. 4 6
      code/AssetLib/glTF/glTFAsset.h
  33. 1 1
      code/AssetLib/glTF/glTFAsset.inl
  34. 1 2
      code/AssetLib/glTF/glTFExporter.cpp
  35. 55 48
      code/AssetLib/glTF2/glTF2Asset.h
  36. 83 65
      code/AssetLib/glTF2/glTF2Asset.inl
  37. 168 131
      code/AssetLib/glTF2/glTF2Exporter.cpp
  38. 17 9
      code/AssetLib/glTF2/glTF2Exporter.h
  39. 63 31
      code/AssetLib/glTF2/glTF2Importer.cpp
  40. 16 2
      code/Common/SceneCombiner.cpp
  41. 8 4
      code/Common/material.cpp
  42. 11 12
      code/Pbrt/PbrtExporter.cpp
  43. 2 2
      code/Pbrt/PbrtExporter.h
  44. 23 15
      code/PostProcessing/EmbedTexturesProcess.cpp
  45. 4 1
      code/PostProcessing/EmbedTexturesProcess.h
  46. 0 1
      contrib/draco/.ruby-version
  47. 0 31
      contrib/draco/.travis.yml
  48. 1 7
      contrib/draco/CMakeLists.txt
  49. 3 3
      contrib/draco/README.md
  50. 8 1
      contrib/draco/cmake/draco_build_definitions.cmake
  51. 0 63
      contrib/draco/cmake/draco_features.cmake
  52. 9 0
      contrib/draco/cmake/draco_flags.cmake
  53. 1 1
      contrib/draco/cmake/draco_install.cmake
  54. 10 10
      contrib/draco/cmake/draco_sanitizer.cmake
  55. 15 9
      contrib/draco/cmake/draco_targets.cmake
  56. 7 7
      contrib/draco/src/draco/core/cycle_timer.cc
  57. 4 3
      contrib/draco/src/draco/core/cycle_timer.h
  58. 2 1
      contrib/draco/src/draco/io/parser_utils.cc
  59. 2 2
      contrib/draco/src/draco/io/ply_reader.cc
  60. 7 0
      contrib/draco/src/draco/io/stdio_file_reader.cc
  61. 1 1
      contrib/poly2tri/poly2tri/sweep/sweep.cc
  62. 105 23
      include/assimp/material.h
  63. 1 1
      include/assimp/metadata.h
  64. 23 23
      include/assimp/pbrmaterial.h
  65. 18 7
      include/assimp/scene.h
  66. BIN
      test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png
  67. BIN
      test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin
  68. 1669 0
      test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf
  69. BIN
      test/models/glTF2/ClearCoat-glTF/PartialCoating.png
  70. BIN
      test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png
  71. BIN
      test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg
  72. BIN
      test/models/glTF2/ClearCoat-glTF/RibsNormal.png
  73. BIN
      test/models/glTF2/ClearCoat-glTF/RoughnessStripes.png
  74. 2 2
      test/unit/utTypes.cpp
  75. 157 20
      test/unit/utglTF2ImportExport.cpp
  76. 3 2
      tools/assimp_view/AnimEvaluator.cpp
  77. 15 9
      tools/assimp_view/AnimEvaluator.h
  78. 3 4
      tools/assimp_view/assimp_view.cpp

+ 2 - 0
CMakeLists.txt

@@ -269,6 +269,8 @@ ELSEIF(MSVC)
     ADD_COMPILE_OPTIONS(/wd4351)
     ADD_COMPILE_OPTIONS(/wd4351)
   ENDIF()
   ENDIF()
   SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od")
   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")
 ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
 ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
   IF(NOT ASSIMP_HUNTER_ENABLED)
   IF(NOT ASSIMP_HUNTER_ENABLED)
     SET(CMAKE_CXX_STANDARD 11)
     SET(CMAKE_CXX_STANDARD 11)

+ 38 - 38
code/AssetLib/3DS/3DSHelper.h

@@ -348,16 +348,16 @@ struct Texture {
         // empty
         // empty
     }
     }
 
 
-    Texture(Texture &&other) AI_NO_EXCEPT : mTextureBlend(std::move(other.mTextureBlend)),
+    Texture(Texture &&other) AI_NO_EXCEPT : mTextureBlend(other.mTextureBlend),
                                             mMapName(std::move(other.mMapName)),
                                             mMapName(std::move(other.mMapName)),
-                                            mOffsetU(std::move(other.mOffsetU)),
-                                            mOffsetV(std::move(other.mOffsetV)),
-                                            mScaleU(std::move(other.mScaleU)),
-                                            mScaleV(std::move(other.mScaleV)),
-                                            mRotation(std::move(other.mRotation)),
-                                            mMapMode(std::move(other.mMapMode)),
-                                            bPrivate(std::move(other.bPrivate)),
-                                            iUVSrc(std::move(other.iUVSrc)) {
+                                            mOffsetU(other.mOffsetU),
+                                            mOffsetV(other.mOffsetV),
+                                            mScaleU(other.mScaleU),
+                                            mScaleV(other.mScaleV),
+                                            mRotation(other.mRotation),
+                                            mMapMode(other.mMapMode),
+                                            bPrivate(other.bPrivate),
+                                            iUVSrc(other.iUVSrc) {
         // empty
         // empty
     }
     }
 
 
@@ -366,16 +366,16 @@ struct Texture {
             return *this;
             return *this;
         }
         }
 
 
-        mTextureBlend = std::move(other.mTextureBlend);
+        mTextureBlend = other.mTextureBlend;
         mMapName = std::move(other.mMapName);
         mMapName = std::move(other.mMapName);
-        mOffsetU = std::move(other.mOffsetU);
-        mOffsetV = std::move(other.mOffsetV);
-        mScaleU = std::move(other.mScaleU);
-        mScaleV = std::move(other.mScaleV);
-        mRotation = std::move(other.mRotation);
-        mMapMode = std::move(other.mMapMode);
-        bPrivate = std::move(other.bPrivate);
-        iUVSrc = std::move(other.iUVSrc);
+        mOffsetU = other.mOffsetU;
+        mOffsetV = other.mOffsetV;
+        mScaleU = other.mScaleU;
+        mScaleV = other.mScaleV;
+        mRotation = other.mRotation;
+        mMapMode = other.mMapMode;
+        bPrivate = other.bPrivate;
+        iUVSrc = other.iUVSrc;
 
 
         return *this;
         return *this;
     }
     }
@@ -461,13 +461,13 @@ struct Material {
 
 
     //! Move constructor. This is explicitly written because MSVC doesn't support defaulting it
     //! Move constructor. This is explicitly written because MSVC doesn't support defaulting it
     Material(Material &&other) AI_NO_EXCEPT : mName(std::move(other.mName)),
     Material(Material &&other) AI_NO_EXCEPT : mName(std::move(other.mName)),
-                                              mDiffuse(std::move(other.mDiffuse)),
-                                              mSpecularExponent(std::move(other.mSpecularExponent)),
-                                              mShininessStrength(std::move(other.mShininessStrength)),
-                                              mSpecular(std::move(other.mSpecular)),
-                                              mAmbient(std::move(other.mAmbient)),
-                                              mShading(std::move(other.mShading)),
-                                              mTransparency(std::move(other.mTransparency)),
+                                              mDiffuse(other.mDiffuse),
+                                              mSpecularExponent(other.mSpecularExponent),
+                                              mShininessStrength(other.mShininessStrength),
+                                              mSpecular(other.mSpecular),
+                                              mAmbient(other.mAmbient),
+                                              mShading(other.mShading),
+                                              mTransparency(other.mTransparency),
                                               sTexDiffuse(std::move(other.sTexDiffuse)),
                                               sTexDiffuse(std::move(other.sTexDiffuse)),
                                               sTexOpacity(std::move(other.sTexOpacity)),
                                               sTexOpacity(std::move(other.sTexOpacity)),
                                               sTexSpecular(std::move(other.sTexSpecular)),
                                               sTexSpecular(std::move(other.sTexSpecular)),
@@ -475,10 +475,10 @@ struct Material {
                                               sTexBump(std::move(other.sTexBump)),
                                               sTexBump(std::move(other.sTexBump)),
                                               sTexEmissive(std::move(other.sTexEmissive)),
                                               sTexEmissive(std::move(other.sTexEmissive)),
                                               sTexShininess(std::move(other.sTexShininess)),
                                               sTexShininess(std::move(other.sTexShininess)),
-                                              mBumpHeight(std::move(other.mBumpHeight)),
-                                              mEmissive(std::move(other.mEmissive)),
+                                              mBumpHeight(other.mBumpHeight),
+                                              mEmissive(other.mEmissive),
                                               sTexAmbient(std::move(other.sTexAmbient)),
                                               sTexAmbient(std::move(other.sTexAmbient)),
-                                              mTwoSided(std::move(other.mTwoSided)) {
+                                              mTwoSided(other.mTwoSided) {
         // empty
         // empty
     }
     }
 
 
@@ -488,13 +488,13 @@ struct Material {
         }
         }
 
 
         mName = std::move(other.mName);
         mName = std::move(other.mName);
-        mDiffuse = std::move(other.mDiffuse);
-        mSpecularExponent = std::move(other.mSpecularExponent);
-        mShininessStrength = std::move(other.mShininessStrength),
-        mSpecular = std::move(other.mSpecular);
-        mAmbient = std::move(other.mAmbient);
-        mShading = std::move(other.mShading);
-        mTransparency = std::move(other.mTransparency);
+        mDiffuse = other.mDiffuse;
+        mSpecularExponent = other.mSpecularExponent;
+        mShininessStrength = other.mShininessStrength,
+        mSpecular = other.mSpecular;
+        mAmbient = other.mAmbient;
+        mShading = other.mShading;
+        mTransparency = other.mTransparency;
         sTexDiffuse = std::move(other.sTexDiffuse);
         sTexDiffuse = std::move(other.sTexDiffuse);
         sTexOpacity = std::move(other.sTexOpacity);
         sTexOpacity = std::move(other.sTexOpacity);
         sTexSpecular = std::move(other.sTexSpecular);
         sTexSpecular = std::move(other.sTexSpecular);
@@ -502,10 +502,10 @@ struct Material {
         sTexBump = std::move(other.sTexBump);
         sTexBump = std::move(other.sTexBump);
         sTexEmissive = std::move(other.sTexEmissive);
         sTexEmissive = std::move(other.sTexEmissive);
         sTexShininess = std::move(other.sTexShininess);
         sTexShininess = std::move(other.sTexShininess);
-        mBumpHeight = std::move(other.mBumpHeight);
-        mEmissive = std::move(other.mEmissive);
+        mBumpHeight = other.mBumpHeight;
+        mEmissive = other.mEmissive;
         sTexAmbient = std::move(other.sTexAmbient);
         sTexAmbient = std::move(other.sTexAmbient);
-        mTwoSided = std::move(other.mTwoSided);
+        mTwoSided = other.mTwoSided;
 
 
         return *this;
         return *this;
     }
     }

+ 4 - 4
code/AssetLib/ASE/ASEParser.h

@@ -95,8 +95,8 @@ struct Material : public D3DS::Material {
     Material(Material &&other) AI_NO_EXCEPT
     Material(Material &&other) AI_NO_EXCEPT
             : D3DS::Material(std::move(other)),
             : D3DS::Material(std::move(other)),
               avSubMaterials(std::move(other.avSubMaterials)),
               avSubMaterials(std::move(other.avSubMaterials)),
-              pcInstance(std::move(other.pcInstance)),
-              bNeed(std::move(other.bNeed)) {
+              pcInstance(other.pcInstance),
+              bNeed(other.bNeed) {
         other.pcInstance = nullptr;
         other.pcInstance = nullptr;
     }
     }
 
 
@@ -108,8 +108,8 @@ struct Material : public D3DS::Material {
         //D3DS::Material::operator=(std::move(other));
         //D3DS::Material::operator=(std::move(other));
 
 
         avSubMaterials = std::move(other.avSubMaterials);
         avSubMaterials = std::move(other.avSubMaterials);
-        pcInstance = std::move(other.pcInstance);
-        bNeed = std::move(other.bNeed);
+        pcInstance = other.pcInstance;
+        bNeed = other.bNeed;
 
 
         other.pcInstance = nullptr;
         other.pcInstance = nullptr;
 
 

+ 1 - 1
code/AssetLib/B3D/B3DImporter.cpp

@@ -143,7 +143,7 @@ AI_WONT_RETURN void B3DImporter::Oops() {
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-AI_WONT_RETURN void B3DImporter::Fail(string str) {
+AI_WONT_RETURN void B3DImporter::Fail(const string &str) {
 #ifdef DEBUG_B3D
 #ifdef DEBUG_B3D
     ASSIMP_LOG_ERROR("Error in B3D file data: ", str);
     ASSIMP_LOG_ERROR("Error in B3D file data: ", str);
 #endif
 #endif

+ 1 - 1
code/AssetLib/B3D/B3DImporter.h

@@ -96,7 +96,7 @@ private:
     };
     };
 
 
     AI_WONT_RETURN void Oops() AI_WONT_RETURN_SUFFIX;
     AI_WONT_RETURN void Oops() AI_WONT_RETURN_SUFFIX;
-    AI_WONT_RETURN void Fail( std::string str ) AI_WONT_RETURN_SUFFIX;
+    AI_WONT_RETURN void Fail(const std::string &str) AI_WONT_RETURN_SUFFIX;
 
 
     void ReadTEXS();
     void ReadTEXS();
     void ReadBRUS();
     void ReadBRUS();

+ 1 - 1
code/AssetLib/Blender/BlenderLoader.cpp

@@ -679,7 +679,7 @@ void BlenderImporter::BuildMaterials(ConversionData &conv_data) {
 
 
     BuildDefaultMaterial(conv_data);
     BuildDefaultMaterial(conv_data);
 
 
-    for (std::shared_ptr<Material> mat : conv_data.materials_raw) {
+    for (const std::shared_ptr<Material> &mat : conv_data.materials_raw) {
 
 
         // reset per material global counters
         // reset per material global counters
         for (size_t i = 0; i < sizeof(conv_data.next_texture) / sizeof(conv_data.next_texture[0]); ++i) {
         for (size_t i = 0; i < sizeof(conv_data.next_texture) / sizeof(conv_data.next_texture[0]); ++i) {

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

@@ -230,7 +230,7 @@ void COBImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-void ConvertTexture(std::shared_ptr<Texture> tex, aiMaterial *out, aiTextureType type) {
+void ConvertTexture(const std::shared_ptr<Texture> &tex, aiMaterial *out, aiTextureType type) {
     const aiString path(tex->path);
     const aiString path(tex->path);
     out->AddProperty(&path, AI_MATKEY_TEXTURE(type, 0));
     out->AddProperty(&path, AI_MATKEY_TEXTURE(type, 0));
     out->AddProperty(&tex->transform, 1, AI_MATKEY_UVTRANSFORM(type, 0));
     out->AddProperty(&tex->transform, 1, AI_MATKEY_UVTRANSFORM(type, 0));

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

@@ -453,7 +453,7 @@ void ColladaParser::PostProcessRootAnimations() {
 
 
         temp.mSubAnims.push_back(clip);
         temp.mSubAnims.push_back(clip);
 
 
-        for (std::string animationID : it.second) {
+        for (const std::string &animationID : it.second) {
             AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID);
             AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID);
 
 
             if (animation != mAnimationLibrary.end()) {
             if (animation != mAnimationLibrary.end()) {

+ 0 - 2
code/AssetLib/DXF/DXFLoader.cpp

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

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

@@ -1766,6 +1766,7 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const TextureMap
         // XXX handle all kinds of UV transformations
         // XXX handle all kinds of UV transformations
         uvTrafo.mScaling = tex->UVScaling();
         uvTrafo.mScaling = tex->UVScaling();
         uvTrafo.mTranslation = tex->UVTranslation();
         uvTrafo.mTranslation = tex->UVTranslation();
+        uvTrafo.mRotation = tex->UVRotation();
         out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, 0);
         out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, 0);
 
 
         const PropertyTable &props = tex->Props();
         const PropertyTable &props = tex->Props();
@@ -1885,6 +1886,7 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const LayeredTex
         // XXX handle all kinds of UV transformations
         // XXX handle all kinds of UV transformations
         uvTrafo.mScaling = tex->UVScaling();
         uvTrafo.mScaling = tex->UVScaling();
         uvTrafo.mTranslation = tex->UVTranslation();
         uvTrafo.mTranslation = tex->UVTranslation();
+        uvTrafo.mRotation = tex->UVRotation();
         out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, texIndex);
         out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, texIndex);
 
 
         const PropertyTable &props = tex->Props();
         const PropertyTable &props = tex->Props();
@@ -2324,6 +2326,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial *out_mat, const PropertyTa
             // XXX handle all kinds of UV transformations
             // XXX handle all kinds of UV transformations
             uvTrafo.mScaling = tex->UVScaling();
             uvTrafo.mScaling = tex->UVScaling();
             uvTrafo.mTranslation = tex->UVTranslation();
             uvTrafo.mTranslation = tex->UVTranslation();
+            uvTrafo.mRotation = tex->UVRotation();
             out_mat->AddProperty(&uvTrafo, 1, (name + "|uvtrafo").c_str(), aiTextureType_UNKNOWN, 0);
             out_mat->AddProperty(&uvTrafo, 1, (name + "|uvtrafo").c_str(), aiTextureType_UNKNOWN, 0);
 
 
             int uvIndex = 0;
             int uvIndex = 0;
@@ -2599,7 +2602,7 @@ void FBXConverter::ConvertAnimationStack(const AnimationStack &st) {
             anim->mMorphMeshChannels = new aiMeshMorphAnim *[numMorphMeshChannels];
             anim->mMorphMeshChannels = new aiMeshMorphAnim *[numMorphMeshChannels];
             anim->mNumMorphMeshChannels = numMorphMeshChannels;
             anim->mNumMorphMeshChannels = numMorphMeshChannels;
             unsigned int i = 0;
             unsigned int i = 0;
-            for (auto morphAnimIt : morphAnimDatas) {
+            for (const auto &morphAnimIt : morphAnimDatas) {
                 morphAnimData *animData = morphAnimIt.second;
                 morphAnimData *animData = morphAnimIt.second;
                 unsigned int numKeys = static_cast<unsigned int>(animData->size());
                 unsigned int numKeys = static_cast<unsigned int>(animData->size());
                 aiMeshMorphAnim *meshMorphAnim = new aiMeshMorphAnim();
                 aiMeshMorphAnim *meshMorphAnim = new aiMeshMorphAnim();

+ 4 - 5
code/AssetLib/FBX/FBXDocument.cpp

@@ -57,9 +57,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/DefaultLogger.hpp>
 
 
-#include <memory>
 #include <functional>
 #include <functional>
 #include <map>
 #include <map>
+#include <memory>
+#include <utility>
 
 
 namespace Assimp {
 namespace Assimp {
 namespace FBX {
 namespace FBX {
@@ -248,10 +249,8 @@ Object::~Object()
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-FileGlobalSettings::FileGlobalSettings(const Document& doc, std::shared_ptr<const PropertyTable> props)
-: props(props)
-, doc(doc)
-{
+FileGlobalSettings::FileGlobalSettings(const Document &doc, std::shared_ptr<const PropertyTable> props) :
+        props(std::move(props)), doc(doc) {
     // empty
     // empty
 }
 }
 
 

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

@@ -500,6 +500,10 @@ public:
         return uvScaling;
         return uvScaling;
     }
     }
 
 
+    const ai_real &UVRotation() const {
+        return uvRotation;
+    }
+
     const PropertyTable& Props() const {
     const PropertyTable& Props() const {
         ai_assert(props.get());
         ai_assert(props.get());
         return *props.get();
         return *props.get();
@@ -517,6 +521,7 @@ public:
 private:
 private:
     aiVector2D uvTrans;
     aiVector2D uvTrans;
     aiVector2D uvScaling;
     aiVector2D uvScaling;
+    ai_real    uvRotation;
 
 
     std::string type;
     std::string type;
     std::string relativeFileName;
     std::string relativeFileName;

+ 2 - 3
code/AssetLib/FBX/FBXExportNode.cpp

@@ -144,9 +144,8 @@ void FBX::Node::AddP70time(
 // public member functions for writing nodes to stream
 // public member functions for writing nodes to stream
 
 
 void FBX::Node::Dump(
 void FBX::Node::Dump(
-    std::shared_ptr<Assimp::IOStream> outfile,
-    bool binary, int indent
-) {
+        const std::shared_ptr<Assimp::IOStream> &outfile,
+        bool binary, int indent) {
     if (binary) {
     if (binary) {
         Assimp::StreamWriterLE outstream(outfile);
         Assimp::StreamWriterLE outstream(outfile);
         DumpBinary(outstream);
         DumpBinary(outstream);

+ 2 - 3
code/AssetLib/FBX/FBXExportNode.h

@@ -157,9 +157,8 @@ public: // member functions for writing data to a file or stream
 
 
     // write the full node to the given file or stream
     // write the full node to the given file or stream
     void Dump(
     void Dump(
-        std::shared_ptr<Assimp::IOStream> outfile,
-        bool binary, int indent
-    );
+            const std::shared_ptr<Assimp::IOStream> &outfile,
+            bool binary, int indent);
     void Dump(Assimp::StreamWriterLE &s, bool binary, int indent);
     void Dump(Assimp::StreamWriterLE &s, bool binary, int indent);
 
 
     // these other functions are for writing data piece by piece.
     // these other functions are for writing data piece by piece.

+ 104 - 29
code/AssetLib/FBX/FBXExporter.cpp

@@ -541,10 +541,17 @@ void FBXExporter::WriteReferences ()
 // (before any actual data is written)
 // (before any actual data is written)
 // ---------------------------------------------------------------
 // ---------------------------------------------------------------
 
 
-size_t count_nodes(const aiNode* n) {
-    size_t count = 1;
+size_t count_nodes(const aiNode* n, const aiNode* root) {
+    size_t count;
+    if (n == root) {
+        count = n->mNumMeshes; // (not counting root node)
+    } else if (n->mNumMeshes > 1) {
+        count = n->mNumMeshes + 1;
+    } else {
+        count = 1;
+    }
     for (size_t i = 0; i < n->mNumChildren; ++i) {
     for (size_t i = 0; i < n->mNumChildren; ++i) {
-        count += count_nodes(n->mChildren[i]);
+        count += count_nodes(n->mChildren[i], root);
     }
     }
     return count;
     return count;
 }
 }
@@ -714,7 +721,7 @@ void FBXExporter::WriteDefinitions ()
 
 
     // Model / FbxNode
     // Model / FbxNode
     // <~~ node hierarchy
     // <~~ node hierarchy
-    count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node)
+    count = int32_t(count_nodes(mScene->mRootNode, mScene->mRootNode));
     if (count) {
     if (count) {
         n = FBX::Node("ObjectType", "Model");
         n = FBX::Node("ObjectType", "Model");
         n.AddChild("Count", count);
         n.AddChild("Count", count);
@@ -1681,6 +1688,10 @@ void FBXExporter::WriteObjects ()
             // link the image data to the texture
             // link the image data to the texture
             connections.emplace_back("C", "OO", image_uid, texture_uid);
             connections.emplace_back("C", "OO", image_uid, texture_uid);
 
 
+            aiUVTransform trafo;
+            unsigned int max = sizeof(aiUVTransform);
+            aiGetMaterialFloatArray(mat, AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE, 0), (float *)&trafo, &max);
+
             // now write the actual texture node
             // now write the actual texture node
             FBX::Node tnode("Texture");
             FBX::Node tnode("Texture");
             // TODO: some way to determine texture name?
             // TODO: some way to determine texture name?
@@ -1691,6 +1702,9 @@ void FBXExporter::WriteObjects ()
             tnode.AddChild("Version", int32_t(202));
             tnode.AddChild("Version", int32_t(202));
             tnode.AddChild("TextureName", texture_name);
             tnode.AddChild("TextureName", texture_name);
             FBX::Node p("Properties70");
             FBX::Node p("Properties70");
+            p.AddP70vectorA("Translation", trafo.mTranslation[0], trafo.mTranslation[1], 0.0);
+            p.AddP70vectorA("Rotation", 0, 0, trafo.mRotation);
+            p.AddP70vectorA("Scaling", trafo.mScaling[0], trafo.mScaling[1], 0.0);
             p.AddP70enum("CurrentTextureBlendMode", 0); // TODO: verify
             p.AddP70enum("CurrentTextureBlendMode", 0); // TODO: verify
             //p.AddP70string("UVSet", ""); // TODO: how should this work?
             //p.AddP70string("UVSet", ""); // TODO: how should this work?
             p.AddP70bool("UseMaterial", 1);
             p.AddP70bool("UseMaterial", 1);
@@ -2196,7 +2210,65 @@ void FBXExporter::WriteObjects ()
         bpnode.Dump(outstream, binary, indent);
         bpnode.Dump(outstream, binary, indent);
     }*/
     }*/
 
 
-    // TODO: cameras, lights
+    // lights
+    indent = 1;
+    lights_uids.clear();
+    for (size_t li = 0; li < mScene->mNumLights; ++li) {
+        aiLight* l = mScene->mLights[li];
+
+        int64_t uid = generate_uid();
+        const std::string lightNodeAttributeName = l->mName.C_Str() + FBX::SEPARATOR + "NodeAttribute";
+
+        FBX::Node lna("NodeAttribute");
+        lna.AddProperties(uid, lightNodeAttributeName, "Light");
+        FBX::Node lnap("Properties70");
+
+        // Light color.
+        lnap.AddP70colorA("Color", l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b);
+
+        // TODO Assimp light description is quite concise and do not handle light intensity.
+        // Default value to 1000W.
+        lnap.AddP70numberA("Intensity", 1000);
+
+        // FBXLight::EType conversion
+        switch (l->mType) {
+        case aiLightSource_POINT:
+            lnap.AddP70enum("LightType", 0);
+            break;
+        case aiLightSource_DIRECTIONAL:
+            lnap.AddP70enum("LightType", 1);
+            break;
+        case aiLightSource_SPOT:
+            lnap.AddP70enum("LightType", 2);
+            lnap.AddP70numberA("InnerAngle", AI_RAD_TO_DEG(l->mAngleInnerCone));
+            lnap.AddP70numberA("OuterAngle", AI_RAD_TO_DEG(l->mAngleOuterCone));
+            break;
+        // TODO Assimp do not handle 'area' nor 'volume' lights, but FBX does.
+        /*case aiLightSource_AREA:
+            lnap.AddP70enum("LightType", 3);
+            lnap.AddP70enum("AreaLightShape", 0); // 0=Rectangle, 1=Sphere
+            break;
+        case aiLightSource_VOLUME:
+            lnap.AddP70enum("LightType", 4);
+            break;*/
+        default:
+            break;
+        }
+
+        // Did not understood how to configure the decay so disabling attenuation.
+        lnap.AddP70enum("DecayType", 0);
+
+        // Dump to FBX stream
+        lna.AddChild(lnap);
+        lna.AddChild("TypeFlags", FBX::FBXExportProperty("Light"));
+        lna.AddChild("GeometryVersion", FBX::FBXExportProperty(int32_t(124)));
+        lna.Dump(outstream, binary, indent);
+
+        // Store name and uid (will be used later when parsing scene nodes)
+        lights_uids[l->mName.C_Str()] = uid;
+    }
+
+    // TODO: cameras
 
 
     // write nodes (i.e. model hierarchy)
     // write nodes (i.e. model hierarchy)
     // start at root node
     // start at root node
@@ -2600,10 +2672,19 @@ void FBXExporter::WriteModelNodes(
         // and connect them
         // and connect them
         connections.emplace_back("C", "OO", node_attribute_uid, node_uid);
         connections.emplace_back("C", "OO", node_attribute_uid, node_uid);
     } else {
     } else {
-        // generate a null node so we can add children to it
-        WriteModelNode(
-            outstream, binary, node, node_uid, "Null", transform_chain
-        );
+        const auto& lightIt = lights_uids.find(node->mName.C_Str());
+        if(lightIt != lights_uids.end()) {
+            // Node has a light connected to it.
+            WriteModelNode(
+                outstream, binary, node, node_uid, "Light", transform_chain
+            );
+            connections.emplace_back("C", "OO", lightIt->second, node_uid);
+        } else {
+            // generate a null node so we can add children to it
+            WriteModelNode(
+                outstream, binary, node, node_uid, "Null", transform_chain
+            );
+        }
     }
     }
 
 
     // if more than one child mesh, make nodes for each mesh
     // if more than one child mesh, make nodes for each mesh
@@ -2625,17 +2706,14 @@ void FBXExporter::WriteModelNodes(
                 ],
                 ],
                 new_node_uid
                 new_node_uid
             );
             );
-            // write model node
-            FBX::Node m("Model");
+
+            aiNode new_node;
             // take name from mesh name, if it exists
             // take name from mesh name, if it exists
-            std::string name = mScene->mMeshes[node->mMeshes[i]]->mName.C_Str();
-            name += FBX::SEPARATOR + "Model";
-            m.AddProperties(new_node_uid, name, "Mesh");
-            m.AddChild("Version", int32_t(232));
-            FBX::Node p("Properties70");
-            p.AddP70enum("InheritType", 1);
-            m.AddChild(p);
-            m.Dump(outstream, binary, 1);
+            new_node.mName = mScene->mMeshes[node->mMeshes[i]]->mName;
+            // write model node
+            WriteModelNode(
+                outstream, binary, &new_node, new_node_uid, "Mesh", std::vector<std::pair<std::string,aiVector3D>>()
+            );
         }
         }
     }
     }
 
 
@@ -2647,16 +2725,14 @@ void FBXExporter::WriteModelNodes(
     }
     }
 }
 }
 
 
-
 void FBXExporter::WriteAnimationCurveNode(
 void FBXExporter::WriteAnimationCurveNode(
-    StreamWriterLE& outstream,
-    int64_t uid,
-    const std::string& name, // "T", "R", or "S"
-    aiVector3D default_value,
-    std::string property_name, // "Lcl Translation" etc
-    int64_t layer_uid,
-    int64_t node_uid
-) {
+        StreamWriterLE &outstream,
+        int64_t uid,
+        const std::string &name, // "T", "R", or "S"
+        aiVector3D default_value,
+        const std::string &property_name, // "Lcl Translation" etc
+        int64_t layer_uid,
+        int64_t node_uid) {
     FBX::Node n("AnimationCurveNode");
     FBX::Node n("AnimationCurveNode");
     n.AddProperties(uid, name + FBX::SEPARATOR + "AnimCurveNode", "");
     n.AddProperties(uid, name + FBX::SEPARATOR + "AnimCurveNode", "");
     FBX::Node p("Properties70");
     FBX::Node p("Properties70");
@@ -2671,7 +2747,6 @@ void FBXExporter::WriteAnimationCurveNode(
     this->connections.emplace_back("C", "OP", uid, node_uid, property_name);
     this->connections.emplace_back("C", "OP", uid, node_uid, property_name);
 }
 }
 
 
-
 void FBXExporter::WriteAnimationCurve(
 void FBXExporter::WriteAnimationCurve(
     StreamWriterLE& outstream,
     StreamWriterLE& outstream,
     double default_value,
     double default_value,

+ 10 - 11
code/AssetLib/FBX/FBXExporter.h

@@ -63,10 +63,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 struct aiScene;
 struct aiScene;
 struct aiNode;
 struct aiNode;
-//struct aiMaterial;
+struct aiLight;
 
 
-namespace Assimp
-{
+namespace Assimp {
     class IOSystem;
     class IOSystem;
     class IOStream;
     class IOStream;
     class ExportProperties;
     class ExportProperties;
@@ -95,6 +94,7 @@ namespace Assimp
         std::vector<int64_t> mesh_uids;
         std::vector<int64_t> mesh_uids;
         std::vector<int64_t> material_uids;
         std::vector<int64_t> material_uids;
         std::map<const aiNode*,int64_t> node_uids;
         std::map<const aiNode*,int64_t> node_uids;
+        std::map<std::string,int64_t> lights_uids;
 
 
         // this crude unique-ID system is actually fine
         // this crude unique-ID system is actually fine
         int64_t last_uid = 999999;
         int64_t last_uid = 999999;
@@ -154,14 +154,13 @@ namespace Assimp
             FBX::TransformInheritance ti_type=FBX::TransformInheritance_RSrs
             FBX::TransformInheritance ti_type=FBX::TransformInheritance_RSrs
         );
         );
         void WriteAnimationCurveNode(
         void WriteAnimationCurveNode(
-            StreamWriterLE& outstream,
-            int64_t uid,
-            const std::string& name, // "T", "R", or "S"
-            aiVector3D default_value,
-            std::string property_name, // "Lcl Translation" etc
-            int64_t animation_layer_uid,
-            int64_t node_uid
-        );
+                StreamWriterLE &outstream,
+                int64_t uid,
+                const std::string &name, // "T", "R", or "S"
+                aiVector3D default_value,
+                const std::string &property_name, // "Lcl Translation" etc
+                int64_t animation_layer_uid,
+                int64_t node_uid);
         void WriteAnimationCurve(
         void WriteAnimationCurve(
             StreamWriterLE& outstream,
             StreamWriterLE& outstream,
             double default_value,
             double default_value,

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

@@ -210,6 +210,11 @@ Texture::Texture(uint64_t id, const Element& element, const Document& doc, const
         uvTrans.y = trans.y;
         uvTrans.y = trans.y;
     }
     }
 
 
+    const aiVector3D &rotation = PropertyGet<aiVector3D>(*props, "Rotation", ok);
+    if (ok) {
+        uvRotation = rotation.z;
+    }
+
     // resolve video links
     // resolve video links
     if(doc.Settings().readTextures) {
     if(doc.Settings().readTextures) {
         const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID());
         const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID());

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

@@ -52,6 +52,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "FBXDocumentUtil.h"
 #include "FBXDocumentUtil.h"
 #include "FBXProperties.h"
 #include "FBXProperties.h"
 
 
+#include <utility>
+
 namespace Assimp {
 namespace Assimp {
 namespace FBX {
 namespace FBX {
 
 
@@ -172,10 +174,8 @@ PropertyTable::PropertyTable()
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-PropertyTable::PropertyTable(const Element& element, std::shared_ptr<const PropertyTable> templateProps)
-: templateProps(templateProps)
-, element(&element)
-{
+PropertyTable::PropertyTable(const Element &element, std::shared_ptr<const PropertyTable> templateProps) :
+        templateProps(std::move(templateProps)), element(&element) {
     const Scope& scope = GetRequiredScope(element);
     const Scope& scope = GetRequiredScope(element);
     for(const ElementMap::value_type& v : scope.Elements()) {
     for(const ElementMap::value_type& v : scope.Elements()) {
         if(v.first != "P") {
         if(v.first != "P") {
@@ -199,7 +199,6 @@ PropertyTable::PropertyTable(const Element& element, std::shared_ptr<const Prope
     }
     }
 }
 }
 
 
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 PropertyTable::~PropertyTable()
 PropertyTable::~PropertyTable()
 {
 {

+ 1 - 1
code/AssetLib/IFC/IFCBoolean.cpp

@@ -513,7 +513,7 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const Schema_2x3::IfcPoly
             }
             }
 
 
             // we got a list of in-out-combinations of intersections. That should be an even number of intersections, or
             // we got a list of in-out-combinations of intersections. That should be an even number of intersections, or
-            // we're fucked.
+            // we are facing a non-recoverable error.
             if ((intersections.size() & 1) != 0) {
             if ((intersections.size() & 1) != 0) {
                 IFCImporter::LogWarn("Odd number of intersections, can't work with that. Omitting half space boundary check.");
                 IFCImporter::LogWarn("Odd number of intersections, can't work with that. Omitting half space boundary check.");
                 continue;
                 continue;

+ 1 - 1
code/AssetLib/IFC/IFCGeometry.cpp

@@ -740,7 +740,7 @@ bool ProcessGeometricItem(const Schema_2x3::IfcRepresentationItem& geo, unsigned
     bool fix_orientation = false;
     bool fix_orientation = false;
     std::shared_ptr< TempMesh > meshtmp = std::make_shared<TempMesh>();
     std::shared_ptr< TempMesh > meshtmp = std::make_shared<TempMesh>();
     if(const Schema_2x3::IfcShellBasedSurfaceModel* shellmod = geo.ToPtr<Schema_2x3::IfcShellBasedSurfaceModel>()) {
     if(const Schema_2x3::IfcShellBasedSurfaceModel* shellmod = geo.ToPtr<Schema_2x3::IfcShellBasedSurfaceModel>()) {
-        for(std::shared_ptr<const Schema_2x3::IfcShell> shell :shellmod->SbsmBoundary) {
+        for (const std::shared_ptr<const Schema_2x3::IfcShell> &shell : shellmod->SbsmBoundary) {
             try {
             try {
                 const ::Assimp::STEP::EXPRESS::ENTITY& e = shell->To<::Assimp::STEP::EXPRESS::ENTITY>();
                 const ::Assimp::STEP::EXPRESS::ENTITY& e = shell->To<::Assimp::STEP::EXPRESS::ENTITY>();
                 const Schema_2x3::IfcConnectedFaceSet& fs = conv.db.MustGetObject(e).To<Schema_2x3::IfcConnectedFaceSet>();
                 const Schema_2x3::IfcConnectedFaceSet& fs = conv.db.MustGetObject(e).To<Schema_2x3::IfcConnectedFaceSet>();

+ 3 - 3
code/AssetLib/IFC/IFCMaterial.cpp

@@ -75,7 +75,7 @@ static void FillMaterial(aiMaterial* mat,const IFC::Schema_2x3::IfcSurfaceStyle*
     mat->AddProperty(&name,AI_MATKEY_NAME);
     mat->AddProperty(&name,AI_MATKEY_NAME);
 
 
     // now see which kinds of surface information are present
     // now see which kinds of surface information are present
-    for(std::shared_ptr< const IFC::Schema_2x3::IfcSurfaceStyleElementSelect > sel2 : surf->Styles) {
+    for (const std::shared_ptr<const IFC::Schema_2x3::IfcSurfaceStyleElementSelect> &sel2 : surf->Styles) {
         if (const IFC::Schema_2x3::IfcSurfaceStyleShading* shade = sel2->ResolveSelectPtr<IFC::Schema_2x3::IfcSurfaceStyleShading>(conv.db)) {
         if (const IFC::Schema_2x3::IfcSurfaceStyleShading* shade = sel2->ResolveSelectPtr<IFC::Schema_2x3::IfcSurfaceStyleShading>(conv.db)) {
             aiColor4D col_base,col;
             aiColor4D col_base,col;
 
 
@@ -124,7 +124,7 @@ static void FillMaterial(aiMaterial* mat,const IFC::Schema_2x3::IfcSurfaceStyle*
                     }
                     }
                 }
                 }
             }
             }
-        } 
+        }
     }
     }
 }
 }
 
 
@@ -134,7 +134,7 @@ unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionDat
     for(;range.first != range.second; ++range.first) {
     for(;range.first != range.second; ++range.first) {
         if(const IFC::Schema_2x3::IfcStyledItem* const styled = conv.db.GetObject((*range.first).second)->ToPtr<IFC::Schema_2x3::IfcStyledItem>()) {
         if(const IFC::Schema_2x3::IfcStyledItem* const styled = conv.db.GetObject((*range.first).second)->ToPtr<IFC::Schema_2x3::IfcStyledItem>()) {
             for(const IFC::Schema_2x3::IfcPresentationStyleAssignment& as : styled->Styles) {
             for(const IFC::Schema_2x3::IfcPresentationStyleAssignment& as : styled->Styles) {
-                for(std::shared_ptr<const IFC::Schema_2x3::IfcPresentationStyleSelect> sel : as.Styles) {
+                for (const std::shared_ptr<const IFC::Schema_2x3::IfcPresentationStyleSelect> &sel : as.Styles) {
 
 
                     if( const IFC::Schema_2x3::IfcSurfaceStyle* const surf = sel->ResolveSelectPtr<IFC::Schema_2x3::IfcSurfaceStyle>(conv.db) ) {
                     if( const IFC::Schema_2x3::IfcSurfaceStyle* const surf = sel->ResolveSelectPtr<IFC::Schema_2x3::IfcSurfaceStyle>(conv.db) ) {
                         // try to satisfy from cache
                         // try to satisfy from cache

+ 6 - 8
code/AssetLib/IFC/IFCUtil.h

@@ -54,6 +54,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/mesh.h>
 #include <assimp/mesh.h>
 #include <assimp/material.h>
 #include <assimp/material.h>
 
 
+#include <utility>
+
 struct aiNode;
 struct aiNode;
 
 
 namespace Assimp {
 namespace Assimp {
@@ -137,14 +139,10 @@ struct TempOpening
     }
     }
 
 
     // ------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------
-    TempOpening(const IFC::Schema_2x3::IfcSolidModel* solid,IfcVector3 extrusionDir,
-        std::shared_ptr<TempMesh> profileMesh,
-        std::shared_ptr<TempMesh> profileMesh2D)
-        : solid(solid)
-        , extrusionDir(extrusionDir)
-        , profileMesh(profileMesh)
-        , profileMesh2D(profileMesh2D)
-    {
+    TempOpening(const IFC::Schema_2x3::IfcSolidModel *solid, IfcVector3 extrusionDir,
+            std::shared_ptr<TempMesh> profileMesh,
+            std::shared_ptr<TempMesh> profileMesh2D) :
+            solid(solid), extrusionDir(extrusionDir), profileMesh(std::move(profileMesh)), profileMesh2D(std::move(profileMesh2D)) {
     }
     }
 
 
     // ------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------

+ 1 - 1
code/AssetLib/LWS/LWSLoader.cpp

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

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

@@ -655,7 +655,7 @@ void M3DImporter::convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned in
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // find a node by name
 // find a node by name
-aiNode *M3DImporter::findNode(aiNode *pNode, aiString name) {
+aiNode *M3DImporter::findNode(aiNode *pNode, const aiString &name) {
     ai_assert(pNode != nullptr);
     ai_assert(pNode != nullptr);
     ai_assert(mScene != nullptr);
     ai_assert(mScene != nullptr);
 
 

+ 2 - 2
code/AssetLib/M3D/M3DImporter.h

@@ -89,8 +89,8 @@ private:
 	// helper functions
 	// helper functions
 	aiColor4D mkColor(uint32_t c);
 	aiColor4D mkColor(uint32_t c);
 	void convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid);
 	void convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid);
-	aiNode *findNode(aiNode *pNode, aiString name);
-	void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m);
+    aiNode *findNode(aiNode *pNode, const aiString &name);
+    void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m);
 	void populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector<aiFace> *faces, std::vector<aiVector3D> *verteces,
 	void populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector<aiFace> *faces, std::vector<aiVector3D> *verteces,
 			std::vector<aiVector3D> *normals, std::vector<aiVector3D> *texcoords, std::vector<aiColor4D> *colors,
 			std::vector<aiVector3D> *normals, std::vector<aiVector3D> *texcoords, std::vector<aiColor4D> *colors,
 			std::vector<unsigned int> *vertexids);
 			std::vector<unsigned int> *vertexids);

+ 2 - 2
code/AssetLib/Ogre/OgreMaterial.cpp

@@ -415,8 +415,8 @@ bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstr
 
 
             // User defined Assimp config property to detect texture type from filename.
             // User defined Assimp config property to detect texture type from filename.
             if (m_detectTextureTypeFromFilename) {
             if (m_detectTextureTypeFromFilename) {
-                size_t posSuffix = textureRef.find_last_of(".");
-                size_t posUnderscore = textureRef.find_last_of("_");
+                size_t posSuffix = textureRef.find_last_of('.');
+                size_t posUnderscore = textureRef.find_last_of('_');
 
 
                 if (posSuffix != string::npos && posUnderscore != string::npos && posSuffix > posUnderscore) {
                 if (posSuffix != string::npos && posUnderscore != string::npos && posSuffix > posUnderscore) {
                     string identifier = ai_tolower(textureRef.substr(posUnderscore, posSuffix - posUnderscore));
                     string identifier = ai_tolower(textureRef.substr(posUnderscore, posSuffix - posUnderscore));

+ 2 - 2
code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp

@@ -99,7 +99,7 @@ static void extractIds(const std::string &key, int &id1, int &id2) {
         return;
         return;
     }
     }
 
 
-    const std::string::size_type pos = key.find(".");
+    const std::string::size_type pos = key.find('.');
     if (std::string::npos == pos) {
     if (std::string::npos == pos) {
         return;
         return;
     }
     }
@@ -208,7 +208,7 @@ void Q3BSPFileImporter::separateMapName(const std::string &importName, std::stri
         return;
         return;
     }
     }
 
 
-    const std::string::size_type pos = importName.rfind(",");
+    const std::string::size_type pos = importName.rfind(',');
     if (std::string::npos == pos) {
     if (std::string::npos == pos) {
         archiveName = importName;
         archiveName = importName;
         return;
         return;

+ 3 - 2
code/AssetLib/STEPParser/STEPFileReader.cpp

@@ -49,8 +49,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "STEPFileEncoding.h"
 #include "STEPFileEncoding.h"
 #include <assimp/TinyFormatter.h>
 #include <assimp/TinyFormatter.h>
 #include <assimp/fast_atof.h>
 #include <assimp/fast_atof.h>
-#include <memory>
 #include <functional>
 #include <functional>
+#include <memory>
+#include <utility>
 
 
 using namespace Assimp;
 using namespace Assimp;
 
 
@@ -87,7 +88,7 @@ static const char *ISO_Token         = "ISO-10303-21;";
 static const char *FILE_SCHEMA_Token = "FILE_SCHEMA";
 static const char *FILE_SCHEMA_Token = "FILE_SCHEMA";
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 STEP::DB* STEP::ReadFileHeader(std::shared_ptr<IOStream> stream) {
 STEP::DB* STEP::ReadFileHeader(std::shared_ptr<IOStream> stream) {
-    std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream));
+    std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(std::move(stream)));
     std::unique_ptr<STEP::DB> db = std::unique_ptr<STEP::DB>(new STEP::DB(reader));
     std::unique_ptr<STEP::DB> db = std::unique_ptr<STEP::DB>(new STEP::DB(reader));
 
 
     LineSplitter &splitter = db->GetSplitter();
     LineSplitter &splitter = db->GetSplitter();

+ 2 - 2
code/AssetLib/Step/STEPFile.h

@@ -634,7 +634,7 @@ private:
 };
 };
 
 
 template <typename T>
 template <typename T>
-inline bool operator==(std::shared_ptr<LazyObject> lo, T whatever) {
+inline bool operator==(const std::shared_ptr<LazyObject> &lo, T whatever) {
     return *lo == whatever; // XXX use std::forward if we have 0x
     return *lo == whatever; // XXX use std::forward if we have 0x
 }
 }
 
 
@@ -816,7 +816,7 @@ public:
     typedef std::pair<RefMap::const_iterator, RefMap::const_iterator> RefMapRange;
     typedef std::pair<RefMap::const_iterator, RefMap::const_iterator> RefMapRange;
 
 
 private:
 private:
-    DB(std::shared_ptr<StreamReaderLE> reader) :
+    DB(const std::shared_ptr<StreamReaderLE> &reader) :
             reader(reader), splitter(*reader, true, true), evaluated_count(), schema(nullptr) {}
             reader(reader), splitter(*reader, true, true), evaluated_count(), schema(nullptr) {}
 
 
 public:
 public:

+ 2 - 2
code/AssetLib/X/XFileExporter.cpp

@@ -530,8 +530,8 @@ void XFileExporter::writePath(const aiString &path)
     while( str.find( "\\\\") != std::string::npos)
     while( str.find( "\\\\") != std::string::npos)
         str.replace( str.find( "\\\\"), 2, "\\");
         str.replace( str.find( "\\\\"), 2, "\\");
 
 
-    while( str.find( "\\") != std::string::npos)
-        str.replace( str.find( "\\"), 1, "/");
+    while (str.find('\\') != std::string::npos)
+        str.replace(str.find('\\'), 1, "/");
 
 
     mOutput << str;
     mOutput << str;
 
 

+ 3 - 3
code/AssetLib/X3D/X3DExporter.hpp

@@ -63,9 +63,9 @@ class X3DExporter {
             // empty
             // empty
         }
         }
 
 
-        SAttribute(SAttribute && rhs) :
-                Name(std::move(rhs.Name)),
-                Value(std::move(rhs.Value)) {
+        SAttribute(SAttribute &&rhs) AI_NO_EXCEPT :
+                Name(rhs.Name),
+                Value(rhs.Value) {
             // empty
             // empty
         }
         }
     };
     };

+ 4 - 6
code/AssetLib/glTF/glTFAsset.h

@@ -456,11 +456,10 @@ namespace glTF
 			/// \param [in] pDecodedData - pointer to decoded data array.
 			/// \param [in] pDecodedData - pointer to decoded data array.
 			/// \param [in] pDecodedData_Length - size of encoded region, in bytes.
 			/// \param [in] pDecodedData_Length - size of encoded region, in bytes.
 			/// \param [in] pID - ID of the region.
 			/// \param [in] pID - ID of the region.
-			SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string pID)
-				: Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), DecodedData_Length(pDecodedData_Length), ID(pID)
-			{}
+            SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID) :
+                    Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), DecodedData_Length(pDecodedData_Length), ID(pID) {}
 
 
-			/// \fn ~SEncodedRegion()
+            /// \fn ~SEncodedRegion()
 			/// Destructor.
 			/// Destructor.
 			~SEncodedRegion() { delete [] DecodedData; }
 			~SEncodedRegion() { delete [] DecodedData; }
 		};
 		};
@@ -1149,8 +1148,7 @@ namespace glTF
 
 
         void ReadExtensionsUsed(Document& doc);
         void ReadExtensionsUsed(Document& doc);
 
 
-
-        IOStream* OpenFile(std::string path, const char* mode, bool absolute = false);
+        IOStream *OpenFile(const std::string &path, const char *mode, bool absolute = false);
     };
     };
 
 
 }
 }

+ 1 - 1
code/AssetLib/glTF/glTFAsset.inl

@@ -1377,7 +1377,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) {
 #undef CHECK_EXT
 #undef CHECK_EXT
 }
 }
 
 
-inline IOStream *Asset::OpenFile(std::string path, const char *mode, bool absolute) {
+inline IOStream *Asset::OpenFile(const std::string& path, const char *mode, bool absolute) {
 #ifdef ASSIMP_API
 #ifdef ASSIMP_API
     (void)absolute;
     (void)absolute;
     return mIOSystem->Open(path, mode);
     return mIOSystem->Open(path, mode);

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

@@ -405,8 +405,7 @@ void glTFExporter::ExportMaterials()
  * Search through node hierarchy and find the node containing the given meshID.
  * Search through node hierarchy and find the node containing the given meshID.
  * Returns true on success, and false otherwise.
  * Returns true on success, and false otherwise.
  */
  */
-bool FindMeshNode(Ref<Node>& nodeIn, Ref<Node>& meshNode, std::string meshID)
-{
+bool FindMeshNode(Ref<Node> &nodeIn, Ref<Node> &meshNode, const std::string &meshID) {
     for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) {
     for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) {
         if (meshID.compare(nodeIn->meshes[i]->id) == 0) {
         if (meshID.compare(nodeIn->meshes[i]->id) == 0) {
           meshNode = nodeIn;
           meshNode = nodeIn;

+ 55 - 48
code/AssetLib/glTF2/glTF2Asset.h

@@ -356,6 +356,53 @@ struct Nullable {
             isPresent(true) {}
             isPresent(true) {}
 };
 };
 
 
+struct CustomExtension {
+    //
+    // A struct containing custom extension data added to a glTF2 file
+    // Has to contain Object, Array, String, Double, Uint64, and Int64 at a minimum
+    // String, Double, Uint64, and Int64 are stored in the Nullables
+    // Object and Array are stored in the std::vector
+    //
+    std::string name;
+
+    Nullable<std::string> mStringValue;
+    Nullable<double> mDoubleValue;
+    Nullable<uint64_t> mUint64Value;
+    Nullable<int64_t> mInt64Value;
+    Nullable<bool> mBoolValue;
+
+    // std::vector<CustomExtension> handles both Object and Array
+    Nullable<std::vector<CustomExtension>> mValues;
+
+    operator bool() const {
+        return Size() != 0;
+    }
+
+    size_t Size() const {
+        if (mValues.isPresent) {
+            return mValues.value.size();
+        } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.isPresent || mBoolValue.isPresent) {
+            return 1;
+        }
+        return 0;
+    }
+
+    CustomExtension() = default;
+    
+    ~CustomExtension() = default;
+    
+    CustomExtension(const CustomExtension &other) :
+            name(other.name),
+            mStringValue(other.mStringValue),
+            mDoubleValue(other.mDoubleValue),
+            mUint64Value(other.mUint64Value),
+            mInt64Value(other.mInt64Value),
+            mBoolValue(other.mBoolValue),
+            mValues(other.mValues) {
+        // empty
+    }
+};
+
 //! Base class for all glTF top-level objects
 //! Base class for all glTF top-level objects
 struct Object {
 struct Object {
     int index; //!< The index of this object within its property container
     int index; //!< The index of this object within its property container
@@ -363,6 +410,9 @@ struct Object {
     std::string id; //!< The globally unique ID used to reference this object
     std::string id; //!< The globally unique ID used to reference this object
     std::string name; //!< The user-defined name of this object
     std::string name; //!< The user-defined name of this object
 
 
+    CustomExtension customExtensions;
+    CustomExtension extras;
+
     //! Objects marked as special are not exported (used to emulate the binary body buffer)
     //! Objects marked as special are not exported (used to emulate the binary body buffer)
     virtual bool IsSpecial() const { return false; }
     virtual bool IsSpecial() const { return false; }
 
 
@@ -377,6 +427,9 @@ struct Object {
     inline Value *FindArray(Value &val, const char *id);
     inline Value *FindArray(Value &val, const char *id);
     inline Value *FindObject(Value &val, const char *id);
     inline Value *FindObject(Value &val, const char *id);
     inline Value *FindExtension(Value &val, const char *extensionId);
     inline Value *FindExtension(Value &val, const char *extensionId);
+
+    inline void ReadExtensions(Value &val);
+    inline void ReadExtras(Value &val);
 };
 };
 
 
 //
 //
@@ -408,7 +461,7 @@ public:
         /// \param [in] pDecodedData - pointer to decoded data array.
         /// \param [in] pDecodedData - pointer to decoded data array.
         /// \param [in] pDecodedData_Length - size of encoded region, in bytes.
         /// \param [in] pDecodedData_Length - size of encoded region, in bytes.
         /// \param [in] pID - ID of the region.
         /// \param [in] pID - ID of the region.
-        SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string pID) :
+        SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID) :
                 Offset(pOffset),
                 Offset(pOffset),
                 EncodedData_Length(pEncodedData_Length),
                 EncodedData_Length(pEncodedData_Length),
                 DecodedData(pDecodedData),
                 DecodedData(pDecodedData),
@@ -834,50 +887,6 @@ struct Mesh : public Object {
     void Read(Value &pJSON_Object, Asset &pAsset_Root);
     void Read(Value &pJSON_Object, Asset &pAsset_Root);
 };
 };
 
 
-struct CustomExtension : public Object {
-    //
-    // A struct containing custom extension data added to a glTF2 file
-    // Has to contain Object, Array, String, Double, Uint64, and Int64 at a minimum
-    // String, Double, Uint64, and Int64 are stored in the Nullables
-    // Object and Array are stored in the std::vector
-    //
-
-    Nullable<std::string> mStringValue;
-    Nullable<double> mDoubleValue;
-    Nullable<uint64_t> mUint64Value;
-    Nullable<int64_t> mInt64Value;
-    Nullable<bool> mBoolValue;
-
-    // std::vector<CustomExtension> handles both Object and Array
-    Nullable<std::vector<CustomExtension>> mValues;
-
-    operator bool() const {
-        return Size() != 0;
-    }
-
-    size_t Size() const {
-        if (mValues.isPresent) {
-            return mValues.value.size();
-        } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.isPresent || mBoolValue.isPresent) {
-            return 1;
-        }
-        return 0;
-    }
-
-    CustomExtension() = default;
-
-    CustomExtension(const CustomExtension &other)
-        : Object(other)
-        , mStringValue(other.mStringValue)
-        , mDoubleValue(other.mDoubleValue)
-        , mUint64Value(other.mUint64Value)
-        , mInt64Value(other.mInt64Value)
-        , mBoolValue(other.mBoolValue)
-        , mValues(other.mValues)
-    {
-    }
-};
-
 struct Node : public Object {
 struct Node : public Object {
     std::vector<Ref<Node>> children;
     std::vector<Ref<Node>> children;
     std::vector<Ref<Mesh>> meshes;
     std::vector<Ref<Mesh>> meshes;
@@ -896,8 +905,6 @@ struct Node : public Object {
 
 
     Ref<Node> parent; //!< This is not part of the glTF specification. Used as a helper.
     Ref<Node> parent; //!< This is not part of the glTF specification. Used as a helper.
 
 
-    CustomExtension extensions;
-
     Node() {}
     Node() {}
     void Read(Value &obj, Asset &r);
     void Read(Value &obj, Asset &r);
 };
 };
@@ -1188,7 +1195,7 @@ private:
     void ReadExtensionsUsed(Document &doc);
     void ReadExtensionsUsed(Document &doc);
     void ReadExtensionsRequired(Document &doc);
     void ReadExtensionsRequired(Document &doc);
 
 
-    IOStream *OpenFile(std::string path, const char *mode, bool absolute = false);
+    IOStream *OpenFile(const std::string &path, const char *mode, bool absolute = false);
 };
 };
 
 
 inline std::string getContextForErrorMessages(const std::string &id, const std::string &name) {
 inline std::string getContextForErrorMessages(const std::string &id, const std::string &name) {

+ 83 - 65
code/AssetLib/glTF2/glTF2Asset.inl

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 
 Copyright (c) 2006-2021, assimp team
 Copyright (c) 2006-2021, assimp team
 
 
-
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,
@@ -304,6 +303,43 @@ inline Value *FindObject(Document &doc, const char *memberId) {
 inline Value *FindExtension(Value &val, const char *extensionId) {
 inline Value *FindExtension(Value &val, const char *extensionId) {
     return FindExtensionInContext(val, extensionId, "the document");
     return FindExtensionInContext(val, extensionId, "the document");
 }
 }
+
+inline CustomExtension ReadExtensions(const char *name, Value &obj) {
+    CustomExtension ret;
+    ret.name = name;
+    if (obj.IsObject()) {
+        ret.mValues.isPresent = true;
+        for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) {
+            auto &val = it->value;
+            ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val));
+        }
+    } else if (obj.IsArray()) {
+        ret.mValues.value.reserve(obj.Size());
+        ret.mValues.isPresent = true;
+        for (unsigned int i = 0; i < obj.Size(); ++i) {
+            ret.mValues.value.push_back(ReadExtensions(name, obj[i]));
+        }
+    } else if (obj.IsNumber()) {
+        if (obj.IsUint64()) {
+            ret.mUint64Value.value = obj.GetUint64();
+            ret.mUint64Value.isPresent = true;
+        } else if (obj.IsInt64()) {
+            ret.mInt64Value.value = obj.GetInt64();
+            ret.mInt64Value.isPresent = true;
+        } else if (obj.IsDouble()) {
+            ret.mDoubleValue.value = obj.GetDouble();
+            ret.mDoubleValue.isPresent = true;
+        }
+    } else if (obj.IsString()) {
+        ReadValue(obj, ret.mStringValue);
+        ret.mStringValue.isPresent = true;
+    } else if (obj.IsBool()) {
+        ret.mBoolValue.value = obj.GetBool();
+        ret.mBoolValue.isPresent = true;
+    }
+    return ret;
+}
+
 } // namespace
 } // namespace
 
 
 inline Value *Object::FindString(Value &val, const char *memberId) {
 inline Value *Object::FindString(Value &val, const char *memberId) {
@@ -330,6 +366,18 @@ inline Value *Object::FindExtension(Value &val, const char *extensionId) {
     return FindExtensionInContext(val, extensionId, id.c_str(), name.c_str());
     return FindExtensionInContext(val, extensionId, id.c_str(), name.c_str());
 }
 }
 
 
+inline void Object::ReadExtensions(Value &val) {
+    if (Value *curExtensions = FindObject(val, "extensions")) {
+        this->customExtensions = glTF2::ReadExtensions("extensions", *curExtensions);
+    }
+}
+
+inline void Object::ReadExtras(Value &val) {
+    if (Value *curExtras = FindObject(val, "extras")) {
+        this->extras = glTF2::ReadExtensions("extras", *curExtras);
+    }
+}
+
 #ifdef ASSIMP_ENABLE_DRACO
 #ifdef ASSIMP_ENABLE_DRACO
 
 
 template <typename T>
 template <typename T>
@@ -569,6 +617,8 @@ Ref<T> LazyDict<T>::Retrieve(unsigned int i) {
     inst->oIndex = i;
     inst->oIndex = i;
     ReadMember(obj, "name", inst->name);
     ReadMember(obj, "name", inst->name);
     inst->Read(obj, mAsset);
     inst->Read(obj, mAsset);
+    inst->ReadExtensions(obj);
+    inst->ReadExtras(obj);
 
 
     Ref<T> result = Add(inst.release());
     Ref<T> result = Add(inst.release());
     mRecursiveReferenceCheck.erase(i);
     mRecursiveReferenceCheck.erase(i);
@@ -733,12 +783,13 @@ inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncod
 }
 }
 
 
 inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) {
 inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) {
-    if ((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) return;
+    if ((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) {
+        return;
+    }
 
 
     for (SEncodedRegion *reg : EncodedRegion_List) {
     for (SEncodedRegion *reg : EncodedRegion_List) {
         if (reg->ID == pID) {
         if (reg->ID == pID) {
             EncodedRegion_Current = reg;
             EncodedRegion_Current = reg;
-
             return;
             return;
         }
         }
     }
     }
@@ -788,10 +839,13 @@ inline bool Buffer::ReplaceData_joint(const size_t pBufferData_Offset, const siz
 }
 }
 
 
 inline size_t Buffer::AppendData(uint8_t *data, size_t length) {
 inline size_t Buffer::AppendData(uint8_t *data, size_t length) {
-    size_t offset = this->byteLength;
+    const size_t offset = this->byteLength;
+    
     // Force alignment to 4 bits
     // Force alignment to 4 bits
-    Grow((length + 3) & ~3);
+    const size_t paddedLength = (length + 3) & ~3;
+    Grow(paddedLength);
     memcpy(mData.get() + offset, data, length);
     memcpy(mData.get() + offset, data, length);
+    memset(mData.get() + offset + length, 0, paddedLength - length);
     return offset;
     return offset;
 }
 }
 
 
@@ -820,9 +874,7 @@ inline void Buffer::Grow(size_t amount) {
 //
 //
 // struct BufferView
 // struct BufferView
 //
 //
-
 inline void BufferView::Read(Value &obj, Asset &r) {
 inline void BufferView::Read(Value &obj, Asset &r) {
-
     if (Value *bufferVal = FindUInt(obj, "buffer")) {
     if (Value *bufferVal = FindUInt(obj, "buffer")) {
         buffer = r.buffers.Retrieve(bufferVal->GetUint());
         buffer = r.buffers.Retrieve(bufferVal->GetUint());
     }
     }
@@ -842,16 +894,21 @@ inline void BufferView::Read(Value &obj, Asset &r) {
 }
 }
 
 
 inline uint8_t *BufferView::GetPointer(size_t accOffset) {
 inline uint8_t *BufferView::GetPointer(size_t accOffset) {
-    if (!buffer) return nullptr;
+    if (!buffer) {
+        return nullptr;
+    }
     uint8_t *basePtr = buffer->GetPointer();
     uint8_t *basePtr = buffer->GetPointer();
-    if (!basePtr) return nullptr;
+    if (!basePtr) {
+        return nullptr;
+    }
 
 
     size_t offset = accOffset + byteOffset;
     size_t offset = accOffset + byteOffset;
     if (buffer->EncodedRegion_Current != nullptr) {
     if (buffer->EncodedRegion_Current != nullptr) {
         const size_t begin = buffer->EncodedRegion_Current->Offset;
         const size_t begin = buffer->EncodedRegion_Current->Offset;
         const size_t end = begin + buffer->EncodedRegion_Current->DecodedData_Length;
         const size_t end = begin + buffer->EncodedRegion_Current->DecodedData_Length;
-        if ((offset >= begin) && (offset < end))
+        if ((offset >= begin) && (offset < end)) {
             return &buffer->EncodedRegion_Current->DecodedData[offset - begin];
             return &buffer->EncodedRegion_Current->DecodedData[offset - begin];
+        }
     }
     }
 
 
     return basePtr + offset;
     return basePtr + offset;
@@ -877,18 +934,18 @@ inline void Accessor::Sparse::PatchData(unsigned int elementSize) {
     while (pIndices != indicesEnd) {
     while (pIndices != indicesEnd) {
         size_t offset;
         size_t offset;
         switch (indicesType) {
         switch (indicesType) {
-        case ComponentType_UNSIGNED_BYTE:
-            offset = *pIndices;
-            break;
-        case ComponentType_UNSIGNED_SHORT:
-            offset = *reinterpret_cast<uint16_t *>(pIndices);
-            break;
-        case ComponentType_UNSIGNED_INT:
-            offset = *reinterpret_cast<uint32_t *>(pIndices);
-            break;
-        default:
-            // have fun with float and negative values from signed types as indices.
-            throw DeadlyImportError("Unsupported component type in index.");
+            case ComponentType_UNSIGNED_BYTE:
+                offset = *pIndices;
+                break;
+            case ComponentType_UNSIGNED_SHORT:
+                offset = *reinterpret_cast<uint16_t *>(pIndices);
+                break;
+            case ComponentType_UNSIGNED_INT:
+                offset = *reinterpret_cast<uint32_t *>(pIndices);
+                break;
+            default:
+                // have fun with float and negative values from signed types as indices.
+                throw DeadlyImportError("Unsupported component type in index.");
         }
         }
 
 
         offset *= elementSize;
         offset *= elementSize;
@@ -900,7 +957,6 @@ inline void Accessor::Sparse::PatchData(unsigned int elementSize) {
 }
 }
 
 
 inline void Accessor::Read(Value &obj, Asset &r) {
 inline void Accessor::Read(Value &obj, Asset &r) {
-
     if (Value *bufferViewVal = FindUInt(obj, "bufferView")) {
     if (Value *bufferViewVal = FindUInt(obj, "bufferView")) {
         bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint());
         bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint());
     }
     }
@@ -1612,9 +1668,9 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
         }
         }
     }
     }
 
 
-    Value *extras = FindObject(pJSON_Object, "extras");
-    if (nullptr != extras) {
-        if (Value *curTargetNames = FindArray(*extras, "targetNames")) {
+    Value *curExtras = FindObject(pJSON_Object, "extras");
+    if (nullptr != curExtras) {
+        if (Value *curTargetNames = FindArray(*curExtras, "targetNames")) {
             this->targetNames.resize(curTargetNames->Size());
             this->targetNames.resize(curTargetNames->Size());
             for (unsigned int i = 0; i < curTargetNames->Size(); ++i) {
             for (unsigned int i = 0; i < curTargetNames->Size(); ++i) {
                 Value &targetNameValue = (*curTargetNames)[i];
                 Value &targetNameValue = (*curTargetNames)[i];
@@ -1683,42 +1739,6 @@ inline void Light::Read(Value &obj, Asset & /*r*/) {
     }
     }
 }
 }
 
 
-inline CustomExtension ReadExtensions(const char *name, Value &obj) {
-    CustomExtension ret;
-    ret.name = name;
-    if (obj.IsObject()) {
-        ret.mValues.isPresent = true;
-        for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) {
-            auto &val = it->value;
-            ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val));
-        }
-    } else if (obj.IsArray()) {
-        ret.mValues.value.reserve(obj.Size());
-        ret.mValues.isPresent = true;
-        for (unsigned int i = 0; i < obj.Size(); ++i) {
-            ret.mValues.value.push_back(ReadExtensions(name, obj[i]));
-        }
-    } else if (obj.IsNumber()) {
-        if (obj.IsUint64()) {
-            ret.mUint64Value.value = obj.GetUint64();
-            ret.mUint64Value.isPresent = true;
-        } else if (obj.IsInt64()) {
-            ret.mInt64Value.value = obj.GetInt64();
-            ret.mInt64Value.isPresent = true;
-        } else if (obj.IsDouble()) {
-            ret.mDoubleValue.value = obj.GetDouble();
-            ret.mDoubleValue.isPresent = true;
-        }
-    } else if (obj.IsString()) {
-        ReadValue(obj, ret.mStringValue);
-        ret.mStringValue.isPresent = true;
-    } else if (obj.IsBool()) {
-        ret.mBoolValue.value = obj.GetBool();
-        ret.mBoolValue.isPresent = true;
-    }
-    return ret;
-}
-
 inline void Node::Read(Value &obj, Asset &r) {
 inline void Node::Read(Value &obj, Asset &r) {
     if (name.empty()) {
     if (name.empty()) {
         name = id;
         name = id;
@@ -1775,8 +1795,6 @@ inline void Node::Read(Value &obj, Asset &r) {
 
 
     Value *curExtensions = FindObject(obj, "extensions");
     Value *curExtensions = FindObject(obj, "extensions");
     if (nullptr != curExtensions) {
     if (nullptr != curExtensions) {
-        this->extensions = ReadExtensions("extensions", *curExtensions);
-
         if (r.extensionsUsed.KHR_lights_punctual) {
         if (r.extensionsUsed.KHR_lights_punctual) {
             if (Value *ext = FindObject(*curExtensions, "KHR_lights_punctual")) {
             if (Value *ext = FindObject(*curExtensions, "KHR_lights_punctual")) {
                 Value *curLight = FindUInt(*ext, "light");
                 Value *curLight = FindUInt(*ext, "light");
@@ -2132,7 +2150,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) {
 #undef CHECK_EXT
 #undef CHECK_EXT
 }
 }
 
 
-inline IOStream *Asset::OpenFile(std::string path, const char *mode, bool /*absolute*/) {
+inline IOStream *Asset::OpenFile(const std::string& path, const char *mode, bool /*absolute*/) {
 #ifdef ASSIMP_API
 #ifdef ASSIMP_API
     return mIOSystem->Open(path, mode);
     return mIOSystem->Open(path, mode);
 #else
 #else

+ 168 - 131
code/AssetLib/glTF2/glTF2Exporter.cpp

@@ -118,14 +118,14 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai
     ExportScene();
     ExportScene();
 
 
     ExportAnimations();
     ExportAnimations();
-    
+
     // export extras
     // export extras
     if(mProperties->HasPropertyCallback("extras"))
     if(mProperties->HasPropertyCallback("extras"))
     {
     {
         std::function<void*(void*)> ExportExtras = mProperties->GetPropertyCallback("extras");
         std::function<void*(void*)> ExportExtras = mProperties->GetPropertyCallback("extras");
         mAsset->extras = (rapidjson::Value*)ExportExtras(0);
         mAsset->extras = (rapidjson::Value*)ExportExtras(0);
     }
     }
-    
+
     AssetWriter writer(*mAsset);
     AssetWriter writer(*mAsset);
 
 
     if (isBinary) {
     if (isBinary) {
@@ -436,11 +436,11 @@ inline void SetSamplerWrap(SamplerWrap& wrap, aiTextureMapMode map)
     };
     };
 }
 }
 
 
-void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref<Texture> texture, aiTextureType tt, unsigned int slot)
+void glTF2Exporter::GetTexSampler(const aiMaterial& mat, Ref<Texture> texture, aiTextureType tt, unsigned int slot)
 {
 {
     aiString aId;
     aiString aId;
     std::string id;
     std::string id;
-    if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) {
+    if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) {
         id = aId.C_Str();
         id = aId.C_Str();
     }
     }
 
 
@@ -455,49 +455,52 @@ void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref<Texture> texture, a
         SamplerMagFilter filterMag;
         SamplerMagFilter filterMag;
         SamplerMinFilter filterMin;
         SamplerMinFilter filterMin;
 
 
-        if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) {
+        if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) {
             SetSamplerWrap(texture->sampler->wrapS, mapU);
             SetSamplerWrap(texture->sampler->wrapS, mapU);
         }
         }
 
 
-        if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) {
+        if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) {
             SetSamplerWrap(texture->sampler->wrapT, mapV);
             SetSamplerWrap(texture->sampler->wrapT, mapV);
         }
         }
 
 
-        if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) {
+        if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) {
             texture->sampler->magFilter = filterMag;
             texture->sampler->magFilter = filterMag;
         }
         }
 
 
-        if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) {
+        if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) {
             texture->sampler->minFilter = filterMin;
             texture->sampler->minFilter = filterMin;
         }
         }
 
 
         aiString name;
         aiString name;
-        if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) {
+        if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) {
             texture->sampler->name = name.C_Str();
             texture->sampler->name = name.C_Str();
         }
         }
     }
     }
 }
 }
 
 
-void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot)
+void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot)
 {
 {
     std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName;
     std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName;
 
 
-    mat->Get(textureKey.c_str(), tt, slot, prop);
+    mat.Get(textureKey.c_str(), tt, slot, prop);
 }
 }
 
 
-void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot)
+void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot)
 {
 {
     std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName;
     std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName;
 
 
-    mat->Get(textureKey.c_str(), tt, slot, prop);
+    mat.Get(textureKey.c_str(), tt, slot, prop);
 }
 }
 
 
-void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref<Texture>& texture, aiTextureType tt, unsigned int slot = 0)
+void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref<Texture>& texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot = 0)
 {
 {
-    if (mat->GetTextureCount(tt) > 0) {
+    if (mat.GetTextureCount(tt) > 0) {
         aiString tex;
         aiString tex;
 
 
-        if (mat->Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) {
+        // Read texcoord (UV map index)
+        mat.Get(AI_MATKEY_UVWSRC(tt, slot), texCoord);
+
+        if (mat.Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) {
             std::string path = tex.C_Str();
             std::string path = tex.C_Str();
 
 
             if (path.size() > 0) {
             if (path.size() > 0) {
@@ -515,11 +518,10 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref<Texture>& texture, aiTe
                     std::string imgId = mAsset->FindUniqueID("", "image");
                     std::string imgId = mAsset->FindUniqueID("", "image");
                     texture->source = mAsset->images.Create(imgId);
                     texture->source = mAsset->images.Create(imgId);
 
 
-                    if (path[0] == '*') { // embedded
-                        aiTexture* curTex = mScene->mTextures[atoi(&path[1])];
-
+                    const aiTexture* curTex = mScene->GetEmbeddedTexture(path.c_str());
+                    if (curTex != nullptr) { // embedded
                         texture->source->name = curTex->mFilename.C_Str();
                         texture->source->name = curTex->mFilename.C_Str();
-                        
+
                         //basisu: embedded ktx2, bu
                         //basisu: embedded ktx2, bu
                         if (curTex->achFormatHint[0]) {
                         if (curTex->achFormatHint[0]) {
                             std::string mimeType = "image/";
                             std::string mimeType = "image/";
@@ -541,7 +543,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref<Texture>& texture, aiTe
                                 mimeType += curTex->achFormatHint;
                                 mimeType += curTex->achFormatHint;
                             texture->source->mimeType = mimeType;
                             texture->source->mimeType = mimeType;
                         }
                         }
-                        
+
                         // The asset has its own buffer, see Image::SetData
                         // The asset has its own buffer, see Image::SetData
                         //basisu: "image/ktx2", "image/basis" as is
                         //basisu: "image/ktx2", "image/basis" as is
                         texture->source->SetData(reinterpret_cast<uint8_t *>(curTex->pcData), curTex->mWidth, *mAsset);
                         texture->source->SetData(reinterpret_cast<uint8_t *>(curTex->pcData), curTex->mWidth, *mAsset);
@@ -554,7 +556,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref<Texture>& texture, aiTe
                             useBasisUniversal = true;
                             useBasisUniversal = true;
                         }
                         }
                     }
                     }
-                    
+
                     //basisu
                     //basisu
                     if(useBasisUniversal) {
                     if(useBasisUniversal) {
                         mAsset->extensionsUsed.KHR_texture_basisu = true;
                         mAsset->extensionsUsed.KHR_texture_basisu = true;
@@ -568,45 +570,45 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref<Texture>& texture, aiTe
     }
     }
 }
 }
 
 
-void glTF2Exporter::GetMatTex(const aiMaterial* mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0)
+void glTF2Exporter::GetMatTex(const aiMaterial& mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0)
 {
 {
     Ref<Texture>& texture = prop.texture;
     Ref<Texture>& texture = prop.texture;
 
 
-    GetMatTex(mat, texture, tt, slot);
+    GetMatTex(mat, texture, prop.texCoord, tt, slot);
 
 
-    if (texture) {
-        GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
-    }
+    //if (texture) {
+    //    GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
+    //}
 }
 }
 
 
-void glTF2Exporter::GetMatTex(const aiMaterial* mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0)
+void glTF2Exporter::GetMatTex(const aiMaterial& mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0)
 {
 {
     Ref<Texture>& texture = prop.texture;
     Ref<Texture>& texture = prop.texture;
 
 
-    GetMatTex(mat, texture, tt, slot);
+    GetMatTex(mat, texture, prop.texCoord, tt, slot);
 
 
     if (texture) {
     if (texture) {
-        GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
+        //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
         GetMatTexProp(mat, prop.scale, "scale", tt, slot);
         GetMatTexProp(mat, prop.scale, "scale", tt, slot);
     }
     }
 }
 }
 
 
-void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0)
+void glTF2Exporter::GetMatTex(const aiMaterial& mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0)
 {
 {
     Ref<Texture>& texture = prop.texture;
     Ref<Texture>& texture = prop.texture;
 
 
-    GetMatTex(mat, texture, tt, slot);
+    GetMatTex(mat, texture, prop.texCoord, tt, slot);
 
 
     if (texture) {
     if (texture) {
-        GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
+        //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
         GetMatTexProp(mat, prop.strength, "strength", tt, slot);
         GetMatTexProp(mat, prop.strength, "strength", tt, slot);
     }
     }
 }
 }
 
 
-aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const char* propName, int type, int idx)
+aiReturn glTF2Exporter::GetMatColor(const aiMaterial& mat, vec4& prop, const char* propName, int type, int idx) const
 {
 {
     aiColor4D col;
     aiColor4D col;
-    aiReturn result = mat->Get(propName, type, idx, col);
+    aiReturn result = mat.Get(propName, type, idx, col);
 
 
     if (result == AI_SUCCESS) {
     if (result == AI_SUCCESS) {
         prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; prop[3] = col.a;
         prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; prop[3] = col.a;
@@ -615,37 +617,116 @@ aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const cha
     return result;
     return result;
 }
 }
 
 
-aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec3& prop, const char* propName, int type, int idx)
+aiReturn glTF2Exporter::GetMatColor(const aiMaterial& mat, vec3& prop, const char* propName, int type, int idx) const
 {
 {
     aiColor3D col;
     aiColor3D col;
-    aiReturn result = mat->Get(propName, type, idx, col);
+    aiReturn result = mat.Get(propName, type, idx, col);
 
 
     if (result == AI_SUCCESS) {
     if (result == AI_SUCCESS) {
-        prop[0] = col.r; prop[1] = col.g; prop[2] = col.b;
+        prop[0] = col.r;
+        prop[1] = col.g;
+        prop[2] = col.b;
     }
     }
 
 
     return result;
     return result;
 }
 }
 
 
+bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG) {
+    bool result = false;
+    // If has Glossiness, a Specular Color or Specular Texture, use the KHR_materials_pbrSpecularGlossiness extension
+    // NOTE: This extension is being considered for deprecation (Dec 2020), may be replaced by KHR_material_specular
+
+    if (mat.Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) == AI_SUCCESS) {
+        result = true;
+    } else {
+        // Don't have explicit glossiness, convert from pbr roughness or legacy shininess
+        float shininess;
+        if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) {
+            pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way
+        } else if (mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) {
+            pbrSG.glossinessFactor = shininess / 1000;
+        }
+    }
+
+    if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) {
+        result = true;
+    }
+    // Add any appropriate textures
+    GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR);
+
+    result = result || pbrSG.specularGlossinessTexture.texture;
+
+    if (result) {
+        // Likely to always have diffuse
+        GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE);
+        GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE);
+    }
+
+    return result;
+}
+
+bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen) {
+    // Return true if got any valid Sheen properties or textures
+    if (GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_SHEEN_COLOR_FACTOR) != aiReturn_SUCCESS)
+        return false;
+
+    // Default Sheen color factor {0,0,0} disables Sheen, so do not export
+    if (sheen.sheenColorFactor == defaultSheenFactor)
+        return false;
+
+    mat.Get(AI_MATKEY_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor);
+
+    GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_SHEEN_COLOR_TEXTURE);
+    GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE);
+
+    return true;
+}
+
+bool glTF2Exporter::GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat) {
+    if (mat.Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor) != aiReturn_SUCCESS) {
+        return false;
+    }
+
+    // Clearcoat factor of zero disables Clearcoat, so do not export
+    if (clearcoat.clearcoatFactor == 0.0f)
+        return false;
+
+    mat.Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor);
+
+    GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_CLEARCOAT_TEXTURE);
+    GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE);
+    GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE);
+
+    return true;
+}
+
+bool glTF2Exporter::GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission) {
+    bool result = mat.Get(AI_MATKEY_TRANSMISSION_FACTOR, transmission.transmissionFactor) == aiReturn_SUCCESS;
+    GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_TRANSMISSION_TEXTURE);
+    return result || transmission.transmissionTexture.texture;
+}
+
 void glTF2Exporter::ExportMaterials()
 void glTF2Exporter::ExportMaterials()
 {
 {
     aiString aiName;
     aiString aiName;
     for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) {
     for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) {
-        const aiMaterial* mat = mScene->mMaterials[i];
+        ai_assert(mScene->mMaterials[i] != nullptr);
+
+        const aiMaterial & mat = *(mScene->mMaterials[i]);
 
 
         std::string id = "material_" + ai_to_string(i);
         std::string id = "material_" + ai_to_string(i);
 
 
         Ref<Material> m = mAsset->materials.Create(id);
         Ref<Material> m = mAsset->materials.Create(id);
 
 
         std::string name;
         std::string name;
-        if (mat->Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) {
+        if (mat.Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) {
             name = aiName.C_Str();
             name = aiName.C_Str();
         }
         }
         name = mAsset->FindUniqueID(name, "material");
         name = mAsset->FindUniqueID(name, "material");
 
 
         m->name = name;
         m->name = name;
 
 
-        GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE);
+        GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_BASE_COLOR);
 
 
         if (!m->pbrMetallicRoughness.baseColorTexture.texture) {
         if (!m->pbrMetallicRoughness.baseColorTexture.texture) {
             //if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture
             //if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture
@@ -654,26 +735,26 @@ void glTF2Exporter::ExportMaterials()
 
 
         GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
         GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
 
 
-        if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR) != AI_SUCCESS) {
+        if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_BASE_COLOR) != AI_SUCCESS) {
             // if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material.
             // if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material.
             //a fallback to any diffuse color should be used instead
             //a fallback to any diffuse color should be used instead
             GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE);
             GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE);
         }
         }
 
 
-        if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) {
+        if (mat.Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) {
             //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0
             //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0
             m->pbrMetallicRoughness.metallicFactor = 0;
             m->pbrMetallicRoughness.metallicFactor = 0;
         }
         }
 
 
         // get roughness if source is gltf2 file
         // get roughness if source is gltf2 file
-        if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) {
+        if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) {
             // otherwise, try to derive and convert from specular + shininess values
             // otherwise, try to derive and convert from specular + shininess values
             aiColor4D specularColor;
             aiColor4D specularColor;
             ai_real shininess;
             ai_real shininess;
 
 
             if (
             if (
-                mat->Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS &&
-                mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS
+                mat.Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS &&
+                mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS
             ) {
             ) {
                 // convert specular color to luminance
                 // convert specular color to luminance
                 float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f;
                 float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f;
@@ -694,103 +775,60 @@ void glTF2Exporter::ExportMaterials()
         GetMatTex(mat, m->emissiveTexture, aiTextureType_EMISSIVE);
         GetMatTex(mat, m->emissiveTexture, aiTextureType_EMISSIVE);
         GetMatColor(mat, m->emissiveFactor, AI_MATKEY_COLOR_EMISSIVE);
         GetMatColor(mat, m->emissiveFactor, AI_MATKEY_COLOR_EMISSIVE);
 
 
-        mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided);
-        mat->Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff);
+        mat.Get(AI_MATKEY_TWOSIDED, m->doubleSided);
+        mat.Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff);
 
 
+        float opacity;
         aiString alphaMode;
         aiString alphaMode;
 
 
-        if (mat->Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) {
-            m->alphaMode = alphaMode.C_Str();
-        } else {
-            float opacity;
-
-            if (mat->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) {
-                if (opacity < 1) {
-                    m->alphaMode = "BLEND";
-                    m->pbrMetallicRoughness.baseColorFactor[3] *= opacity;
-                }
+        if (mat.Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) {
+            if (opacity < 1) {
+                m->alphaMode = "BLEND";
+                m->pbrMetallicRoughness.baseColorFactor[3] *= opacity;
             }
             }
         }
         }
+        if (mat.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) {
+            m->alphaMode = alphaMode.C_Str();
+        }
 
 
-        bool hasPbrSpecularGlossiness = false;
-        mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS, hasPbrSpecularGlossiness);
-
-        if (hasPbrSpecularGlossiness) {
-
-            if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) {
-                mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true;
-            }
-
+        {
+            // KHR_materials_pbrSpecularGlossiness extension
+            // NOTE: This extension is being considered for deprecation (Dec 2020)
             PbrSpecularGlossiness pbrSG;
             PbrSpecularGlossiness pbrSG;
-
-            GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE);
-            GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR);
-
-            if (mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) {
-				float shininess;
-
-				if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) {
-                    pbrSG.glossinessFactor = shininess / 1000;
-                }
+            if (GetMatSpecGloss(mat, pbrSG)) {
+                mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true;
+                m->pbrSpecularGlossiness = Nullable<PbrSpecularGlossiness>(pbrSG);
             }
             }
-
-            GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE);
-            GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR);
-
-            m->pbrSpecularGlossiness = Nullable<PbrSpecularGlossiness>(pbrSG);
         }
         }
 
 
-        bool unlit;
-        if (mat->Get(AI_MATKEY_GLTF_UNLIT, unlit) == AI_SUCCESS && unlit) {
+        // glTFv2 is either PBR or Unlit
+        aiShadingMode shadingMode = aiShadingMode_PBR_BRDF;
+        mat.Get(AI_MATKEY_SHADING_MODEL, shadingMode);
+        if (shadingMode == aiShadingMode_Unlit) {
             mAsset->extensionsUsed.KHR_materials_unlit = true;
             mAsset->extensionsUsed.KHR_materials_unlit = true;
             m->unlit = true;
             m->unlit = true;
-        }
-
-        bool hasMaterialSheen = false;
-        mat->Get(AI_MATKEY_GLTF_MATERIAL_SHEEN, hasMaterialSheen);
-
-        if (hasMaterialSheen) {
-            mAsset->extensionsUsed.KHR_materials_sheen = true;
-
-            MaterialSheen sheen;
-
-            GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR);
-            mat->Get(AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor);
-            GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE);
-            GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE);
-
-            m->materialSheen = Nullable<MaterialSheen>(sheen);
-        }
-
-        bool hasMaterialClearcoat = false;
-        mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT, hasMaterialClearcoat);
-
-        if (hasMaterialClearcoat) {
-            mAsset->extensionsUsed.KHR_materials_clearcoat= true;
-
-            MaterialClearcoat clearcoat;
-
-            mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor);
-            mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor);
-            GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE);
-            GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE);
-            GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE);
-
-            m->materialClearcoat = Nullable<MaterialClearcoat>(clearcoat);
-        }
-
-        bool hasMaterialTransmission = false;
-        mat->Get(AI_MATKEY_GLTF_MATERIAL_TRANSMISSION, hasMaterialTransmission);
-
-        if (hasMaterialTransmission) {
-            mAsset->extensionsUsed.KHR_materials_transmission = true;
-
-            MaterialTransmission transmission;
+        } else {
+            // These extensions are not compatible with KHR_materials_unlit or KHR_materials_pbrSpecularGlossiness
+            if (!m->pbrSpecularGlossiness.isPresent) {
+                // Sheen
+                MaterialSheen sheen;
+                if (GetMatSheen(mat, sheen)) {
+                    mAsset->extensionsUsed.KHR_materials_sheen = true;
+                    m->materialSheen = Nullable<MaterialSheen>(sheen);
+                }
 
 
-            mat->Get(AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR, transmission.transmissionFactor);
-            GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE);
+                MaterialClearcoat clearcoat;
+                if (GetMatClearcoat(mat, clearcoat)) {
+                    mAsset->extensionsUsed.KHR_materials_clearcoat = true;
+                    m->materialClearcoat = Nullable<MaterialClearcoat>(clearcoat);
+                }
 
 
-            m->materialTransmission = Nullable<MaterialTransmission>(transmission);
+                MaterialTransmission transmission;
+                if (GetMatTransmission(mat, transmission)) {
+                    mAsset->extensionsUsed.KHR_materials_transmission = true;
+                    m->materialTransmission = Nullable<MaterialTransmission>(transmission);
+                }
+            }
         }
         }
     }
     }
 }
 }
@@ -799,8 +837,7 @@ void glTF2Exporter::ExportMaterials()
  * Search through node hierarchy and find the node containing the given meshID.
  * Search through node hierarchy and find the node containing the given meshID.
  * Returns true on success, and false otherwise.
  * Returns true on success, and false otherwise.
  */
  */
-bool FindMeshNode(Ref<Node>& nodeIn, Ref<Node>& meshNode, std::string meshID)
-{
+bool FindMeshNode(Ref<Node> &nodeIn, Ref<Node> &meshNode, const std::string &meshID) {
     for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) {
     for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) {
         if (meshID.compare(nodeIn->meshes[i]->id) == 0) {
         if (meshID.compare(nodeIn->meshes[i]->id) == 0) {
             meshNode = nodeIn;
             meshNode = nodeIn;

+ 17 - 9
code/AssetLib/glTF2/glTF2Exporter.h

@@ -72,6 +72,10 @@ namespace glTF2
     struct OcclusionTextureInfo;
     struct OcclusionTextureInfo;
     struct Node;
     struct Node;
     struct Texture;
     struct Texture;
+    struct PbrSpecularGlossiness;
+    struct MaterialSheen;
+    struct MaterialClearcoat;
+    struct MaterialTransmission;
 
 
     // Vec/matrix types, as raw float arrays
     // Vec/matrix types, as raw float arrays
 	typedef float (vec2)[2];
 	typedef float (vec2)[2];
@@ -97,15 +101,19 @@ namespace Assimp
 
 
     protected:
     protected:
         void WriteBinaryData(IOStream* outfile, std::size_t sceneLength);
         void WriteBinaryData(IOStream* outfile, std::size_t sceneLength);
-        void GetTexSampler(const aiMaterial* mat, glTF2::Ref<glTF2::Texture> texture, aiTextureType tt, unsigned int slot);
-        void GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx);
-        void GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx);
-        void GetMatTex(const aiMaterial* mat, glTF2::Ref<glTF2::Texture>& texture, aiTextureType tt, unsigned int slot);
-        void GetMatTex(const aiMaterial* mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot);
-        void GetMatTex(const aiMaterial* mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot);
-        void GetMatTex(const aiMaterial* mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot);
-        aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec4& prop, const char* propName, int type, int idx);
-        aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec3& prop, const char* propName, int type, int idx);
+        void GetTexSampler(const aiMaterial& mat, glTF2::Ref<glTF2::Texture> texture, aiTextureType tt, unsigned int slot);
+        void GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx);
+        void GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx);
+        void GetMatTex(const aiMaterial& mat, glTF2::Ref<glTF2::Texture>& texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot);
+        void GetMatTex(const aiMaterial& mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot);
+        void GetMatTex(const aiMaterial& mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot);
+        void GetMatTex(const aiMaterial& mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot);
+        aiReturn GetMatColor(const aiMaterial& mat, glTF2::vec4& prop, const char* propName, int type, int idx) const;
+        aiReturn GetMatColor(const aiMaterial& mat, glTF2::vec3& prop, const char* propName, int type, int idx) const;
+        bool GetMatSpecGloss(const aiMaterial& mat, glTF2::PbrSpecularGlossiness& pbrSG);
+        bool GetMatSheen(const aiMaterial& mat, glTF2::MaterialSheen& sheen);
+        bool GetMatClearcoat(const aiMaterial& mat, glTF2::MaterialClearcoat& clearcoat);
+        bool GetMatTransmission(const aiMaterial& mat, glTF2::MaterialTransmission& transmission);
         void ExportMetadata();
         void ExportMetadata();
         void ExportMaterials();
         void ExportMaterials();
         void ExportMeshes();
         void ExportMeshes();

+ 63 - 31
code/AssetLib/glTF2/glTF2Importer.cpp

@@ -165,7 +165,8 @@ inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset
         }
         }
 
 
         mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot));
         mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot));
-        mat->AddProperty(&prop.texCoord, 1, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(texType, texSlot));
+        const int uvIndex = static_cast<int>(prop.texCoord);
+        mat->AddProperty(&uvIndex, 1, AI_MATKEY_UVWSRC(texType, texSlot));
 
 
         if (prop.textureTransformSupported) {
         if (prop.textureTransformSupported) {
             aiUVTransform transform;
             aiUVTransform transform;
@@ -208,6 +209,11 @@ inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset
             if (sampler->minFilter != SamplerMinFilter::UNSET) {
             if (sampler->minFilter != SamplerMinFilter::UNSET) {
                 mat->AddProperty(&sampler->minFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(texType, texSlot));
                 mat->AddProperty(&sampler->minFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(texType, texSlot));
             }
             }
+        } else {
+            // Use glTFv2 default sampler
+            const aiTextureMapMode default_wrap = aiTextureMapMode_Wrap;
+            mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_U(texType, texSlot));
+            mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_V(texType, texSlot));
         }
         }
     }
     }
 }
 }
@@ -238,16 +244,18 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
             aimat->AddProperty(&str, AI_MATKEY_NAME);
             aimat->AddProperty(&str, AI_MATKEY_NAME);
         }
         }
 
 
+        // Set Assimp DIFFUSE and BASE COLOR to the pbrMetallicRoughness base color and texture for backwards compatibility
+        // Technically should not load any pbrMetallicRoughness if extensionsRequired contains KHR_materials_pbrSpecularGlossiness
         SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE);
         SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE);
-        SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR);
+        SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_BASE_COLOR);
 
 
         SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE);
         SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE);
-        SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE);
+        SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_BASE_COLOR);
 
 
         SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
         SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
 
 
-        aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR);
-        aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR);
+        aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_METALLIC_FACTOR);
+        aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_ROUGHNESS_FACTOR);
 
 
         float roughnessAsShininess = 1 - mat.pbrMetallicRoughness.roughnessFactor;
         float roughnessAsShininess = 1 - mat.pbrMetallicRoughness.roughnessFactor;
         roughnessAsShininess *= roughnessAsShininess * 1000;
         roughnessAsShininess *= roughnessAsShininess * 1000;
@@ -259,6 +267,7 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
         SetMaterialColorProperty(r, mat.emissiveFactor, aimat, AI_MATKEY_COLOR_EMISSIVE);
         SetMaterialColorProperty(r, mat.emissiveFactor, aimat, AI_MATKEY_COLOR_EMISSIVE);
 
 
         aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED);
         aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED);
+        aimat->AddProperty(&mat.pbrMetallicRoughness.baseColorFactor[3], 1, AI_MATKEY_OPACITY);
 
 
         aiString alphaMode(mat.alphaMode);
         aiString alphaMode(mat.alphaMode);
         aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE);
         aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE);
@@ -268,52 +277,58 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
         if (mat.pbrSpecularGlossiness.isPresent) {
         if (mat.pbrSpecularGlossiness.isPresent) {
             PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value;
             PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value;
 
 
-            aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS);
             SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE);
             SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE);
             SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_COLOR_SPECULAR);
             SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_COLOR_SPECULAR);
 
 
             float glossinessAsShininess = pbrSG.glossinessFactor * 1000.0f;
             float glossinessAsShininess = pbrSG.glossinessFactor * 1000.0f;
             aimat->AddProperty(&glossinessAsShininess, 1, AI_MATKEY_SHININESS);
             aimat->AddProperty(&glossinessAsShininess, 1, AI_MATKEY_SHININESS);
-            aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR);
+            aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLOSSINESS_FACTOR);
 
 
             SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, aiTextureType_DIFFUSE);
             SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, aiTextureType_DIFFUSE);
 
 
             SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, aiTextureType_SPECULAR);
             SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, aiTextureType_SPECULAR);
         }
         }
+
+        // glTFv2 is either PBR or Unlit
+        aiShadingMode shadingMode = aiShadingMode_PBR_BRDF;
         if (mat.unlit) {
         if (mat.unlit) {
-            aimat->AddProperty(&mat.unlit, 1, AI_MATKEY_GLTF_UNLIT);
+            shadingMode = aiShadingMode_Unlit;
         }
         }
 
 
-        //KHR_materials_sheen
+        aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL);
+
+
+        // KHR_materials_sheen
         if (mat.materialSheen.isPresent) {
         if (mat.materialSheen.isPresent) {
             MaterialSheen &sheen = mat.materialSheen.value;
             MaterialSheen &sheen = mat.materialSheen.value;
-
-            aimat->AddProperty(&mat.materialSheen.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_SHEEN);
-            SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR);
-            aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR);
-            SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE);
-            SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE);
+            // Default value {0,0,0} disables Sheen
+            if (sheen.sheenColorFactor != defaultSheenFactor) {
+                SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_SHEEN_COLOR_FACTOR);
+                aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_SHEEN_ROUGHNESS_FACTOR);
+                SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_SHEEN_COLOR_TEXTURE);
+                SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE);
+            }
         }
         }
 
 
-        //KHR_materials_clearcoat
+        // KHR_materials_clearcoat
         if (mat.materialClearcoat.isPresent) {
         if (mat.materialClearcoat.isPresent) {
             MaterialClearcoat &clearcoat = mat.materialClearcoat.value;
             MaterialClearcoat &clearcoat = mat.materialClearcoat.value;
-
-            aimat->AddProperty(&mat.materialClearcoat.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT);
-            aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR);
-            aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR);
-            SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE);
-            SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE);
-            SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE);
+            // Default value 0.0 disables clearcoat
+            if (clearcoat.clearcoatFactor != 0.0f) {
+                aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_CLEARCOAT_FACTOR);
+                aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR);
+                SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_CLEARCOAT_TEXTURE);
+                SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE);
+                SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE);
+            }
         }
         }
 
 
-        //KHR_materials_transmission
+        // KHR_materials_transmission
         if (mat.materialTransmission.isPresent) {
         if (mat.materialTransmission.isPresent) {
             MaterialTransmission &transmission = mat.materialTransmission.value;
             MaterialTransmission &transmission = mat.materialTransmission.value;
 
 
-            aimat->AddProperty(&mat.materialTransmission.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION);
-            aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR);
-            SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE);
+            aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_TRANSMISSION_FACTOR);
+            SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_TRANSMISSION_TEXTURE);
         }
         }
 
 
         return aimat;
         return aimat;
@@ -489,7 +504,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                                                "\" does not match the vertex count");
                                                "\" does not match the vertex count");
                     continue;
                     continue;
                 }
                 }
-                
+
                 auto componentType = attr.color[c]->componentType;
                 auto componentType = attr.color[c]->componentType;
                 if (componentType == glTF2::ComponentType_FLOAT) {
                 if (componentType == glTF2::ComponentType_FLOAT) {
                     attr.color[c]->ExtractData(aim->mColors[c]);
                     attr.color[c]->ExtractData(aim->mColors[c]);
@@ -984,6 +999,14 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) {
     }
     }
 }
 }
 
 
+void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) {
+    if (extension.mValues.isPresent) {
+        for (size_t i = 0; i < extension.mValues.value.size(); ++i) {
+            ParseExtensions(metadata, extension.mValues.value[i]);
+        }
+    }
+}
+
 aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &meshOffsets, glTF2::Ref<glTF2::Node> &ptr) {
 aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &meshOffsets, glTF2::Ref<glTF2::Node> &ptr) {
     Node &node = *ptr;
     Node &node = *ptr;
 
 
@@ -1002,9 +1025,14 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
             }
             }
         }
         }
 
 
-        if (node.extensions) {
+        if (node.customExtensions || node.extras) {
             ainode->mMetaData = new aiMetadata;
             ainode->mMetaData = new aiMetadata;
-            ParseExtensions(ainode->mMetaData, node.extensions);
+            if (node.customExtensions) {
+                ParseExtensions(ainode->mMetaData, node.customExtensions);
+            }
+            if (node.extras) {
+                ParseExtras(ainode->mMetaData, node.extras);
+            }
         }
         }
 
 
         GetNodeTransform(ainode->mTransformation, node);
         GetNodeTransform(ainode->mTransformation, node);
@@ -1498,7 +1526,8 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) {
     const bool hasVersion = !a.asset.version.empty();
     const bool hasVersion = !a.asset.version.empty();
     const bool hasGenerator = !a.asset.generator.empty();
     const bool hasGenerator = !a.asset.generator.empty();
     const bool hasCopyright = !a.asset.copyright.empty();
     const bool hasCopyright = !a.asset.copyright.empty();
-    if (hasVersion || hasGenerator || hasCopyright) {
+    const bool hasSceneMetadata = a.scene->customExtensions;
+    if (hasVersion || hasGenerator || hasCopyright || hasSceneMetadata) {
         mScene->mMetaData = new aiMetadata;
         mScene->mMetaData = new aiMetadata;
         if (hasVersion) {
         if (hasVersion) {
             mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version));
             mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version));
@@ -1509,6 +1538,9 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) {
         if (hasCopyright) {
         if (hasCopyright) {
             mScene->mMetaData->Add(AI_METADATA_SOURCE_COPYRIGHT, aiString(a.asset.copyright));
             mScene->mMetaData->Add(AI_METADATA_SOURCE_COPYRIGHT, aiString(a.asset.copyright));
         }
         }
+        if (hasSceneMetadata) {
+            ParseExtensions(mScene->mMetaData, a.scene->customExtensions);
+        }
     }
     }
 }
 }
 
 

+ 16 - 2
code/Common/SceneCombiner.cpp

@@ -406,11 +406,25 @@ void SceneCombiner::MergeScenes(aiScene **_dest, aiScene *master, std::vector<At
                             // Check whether this texture is an embedded texture.
                             // Check whether this texture is an embedded texture.
                             // In this case the property looks like this: *<n>,
                             // In this case the property looks like this: *<n>,
                             // where n is the index of the texture.
                             // where n is the index of the texture.
-                            aiString &s = *((aiString *)prop->mData);
+                            // Copy here because we overwrite the string data in-place and the buffer inside of aiString
+                            // will be a lie if we just reinterpret from prop->mData. The size of mData is not guaranteed to be
+                            // MAXLEN in size.
+                            aiString s(*(aiString *)prop->mData);
                             if ('*' == s.data[0]) {
                             if ('*' == s.data[0]) {
                                 // Offset the index and write it back ..
                                 // Offset the index and write it back ..
                                 const unsigned int idx = strtoul10(&s.data[1]) + offset[n];
                                 const unsigned int idx = strtoul10(&s.data[1]) + offset[n];
-                                ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx);
+                                const unsigned int oldLen = s.length;
+
+                                s.length = 1 + ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx);
+
+                                // The string changed in size so we need to reallocate the buffer for the property.
+                                if (oldLen < s.length) {
+                                    prop->mDataLength += s.length - oldLen;
+                                    delete[] prop->mData;
+                                    prop->mData = new char[prop->mDataLength];
+                                }
+
+                                memcpy(prop->mData, static_cast<void*>(&s), prop->mDataLength);
                             }
                             }
                         }
                         }
 
 

+ 8 - 4
code/Common/material.cpp

@@ -47,10 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/material.h>
 #include <assimp/material.h>
 
 
 // -------------------------------------------------------------------------------
 // -------------------------------------------------------------------------------
-const char* TextureTypeToString(aiTextureType in)
-{
-    switch (in)
-    {
+const char *TextureTypeToString(aiTextureType in) {
+    switch (in) {
     case aiTextureType_NONE:
     case aiTextureType_NONE:
         return "n/a";
         return "n/a";
     case aiTextureType_DIFFUSE:
     case aiTextureType_DIFFUSE:
@@ -87,6 +85,12 @@ const char* TextureTypeToString(aiTextureType in)
         return "DiffuseRoughness";
         return "DiffuseRoughness";
     case aiTextureType_AMBIENT_OCCLUSION:
     case aiTextureType_AMBIENT_OCCLUSION:
         return "AmbientOcclusion";
         return "AmbientOcclusion";
+    case aiTextureType_SHEEN:
+        return "Sheen";
+    case aiTextureType_CLEARCOAT:
+        return "Clearcoat";
+    case aiTextureType_TRANSMISSION:
+        return "Transmission";
     case aiTextureType_UNKNOWN:
     case aiTextureType_UNKNOWN:
         return "Unknown";
         return "Unknown";
     default:
     default:

+ 11 - 12
code/Pbrt/PbrtExporter.cpp

@@ -105,14 +105,13 @@ void ExportScenePbrt (
 } // end of namespace Assimp
 } // end of namespace Assimp
 
 
 // Constructor
 // Constructor
-PbrtExporter::PbrtExporter (
-    const aiScene* pScene, IOSystem* pIOSystem,
-    const std::string path, const std::string file)
-: mScene(pScene),
-  mIOSystem(pIOSystem),
-  mPath(path),
-  mFile(file)
-{
+PbrtExporter::PbrtExporter(
+        const aiScene *pScene, IOSystem *pIOSystem,
+        const std::string &path, const std::string &file) :
+        mScene(pScene),
+        mIOSystem(pIOSystem),
+        mPath(path),
+        mFile(file) {
     // Export embedded textures.
     // Export embedded textures.
     if (mScene->mNumTextures > 0)
     if (mScene->mNumTextures > 0)
         if (!mIOSystem->CreateDirectory("textures"))
         if (!mIOSystem->CreateDirectory("textures"))
@@ -209,12 +208,12 @@ void PbrtExporter::WriteMetaData() {
                 aiString* value =
                 aiString* value =
                     static_cast<aiString*>(pMetaData->mValues[i].mData);
                     static_cast<aiString*>(pMetaData->mValues[i].mData);
                 std::string svalue = value->C_Str();
                 std::string svalue = value->C_Str();
-                std::size_t found = svalue.find_first_of("\n");
+                std::size_t found = svalue.find_first_of('\n');
                 mOutput << "\n";
                 mOutput << "\n";
                 while (found != std::string::npos) {
                 while (found != std::string::npos) {
                     mOutput << "#     " << svalue.substr(0, found) << "\n";
                     mOutput << "#     " << svalue.substr(0, found) << "\n";
                     svalue = svalue.substr(found + 1);
                     svalue = svalue.substr(found + 1);
-                    found = svalue.find_first_of("\n");
+                    found = svalue.find_first_of('\n');
                 }
                 }
                 mOutput << "#     " << svalue << "\n";
                 mOutput << "#     " << svalue << "\n";
                 break;
                 break;
@@ -595,8 +594,8 @@ void PbrtExporter::WriteMaterial(int m) {
     }
     }
     mOutput << "\n";
     mOutput << "\n";
 
 
-    auto White = [](aiColor3D c) { return c.r == 1 && c.g == 1 && c.b == 1; };
-    auto Black = [](aiColor3D c) { return c.r == 0 && c.g == 0 && c.b == 0; };
+    auto White = [](const aiColor3D &c) { return c.r == 1 && c.g == 1 && c.b == 1; };
+    auto Black = [](const aiColor3D &c) { return c.r == 0 && c.g == 0 && c.b == 0; };
 
 
     aiColor3D diffuse, specular, transparency;
     aiColor3D diffuse, specular, transparency;
     bool constantDiffuse = (material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse) == AI_SUCCESS &&
     bool constantDiffuse = (material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse) == AI_SUCCESS &&

+ 2 - 2
code/Pbrt/PbrtExporter.h

@@ -74,8 +74,8 @@ class PbrtExporter
 {
 {
 public:
 public:
     /// Constructor for a specific scene to export
     /// Constructor for a specific scene to export
-    PbrtExporter(const aiScene* pScene, IOSystem* pIOSystem,
-        const std::string path, const std::string file);
+    PbrtExporter(const aiScene *pScene, IOSystem *pIOSystem,
+            const std::string &path, const std::string &file);
 
 
     /// Destructor
     /// Destructor
     virtual ~PbrtExporter();
     virtual ~PbrtExporter();

+ 23 - 15
code/PostProcessing/EmbedTexturesProcess.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 
 Copyright (c) 2006-2021, assimp team
 Copyright (c) 2006-2021, assimp team
 
 
-
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,
@@ -41,6 +40,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 */
 
 
 #include "EmbedTexturesProcess.h"
 #include "EmbedTexturesProcess.h"
+#include <assimp/IOStream.hpp>
+#include <assimp/IOSystem.hpp>
 #include <assimp/ParsingUtils.h>
 #include <assimp/ParsingUtils.h>
 #include "ProcessHelper.h"
 #include "ProcessHelper.h"
 
 
@@ -48,11 +49,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 using namespace Assimp;
 using namespace Assimp;
 
 
-EmbedTexturesProcess::EmbedTexturesProcess()
-: BaseProcess() {
+EmbedTexturesProcess::EmbedTexturesProcess() :
+        BaseProcess() {
+    // empty
 }
 }
 
 
 EmbedTexturesProcess::~EmbedTexturesProcess() {
 EmbedTexturesProcess::~EmbedTexturesProcess() {
+    // empty
 }
 }
 
 
 bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const {
 bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const {
@@ -62,15 +65,16 @@ bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const {
 void EmbedTexturesProcess::SetupProperties(const Importer* pImp) {
 void EmbedTexturesProcess::SetupProperties(const Importer* pImp) {
     mRootPath = pImp->GetPropertyString("sourceFilePath");
     mRootPath = pImp->GetPropertyString("sourceFilePath");
     mRootPath = mRootPath.substr(0, mRootPath.find_last_of("\\/") + 1u);
     mRootPath = mRootPath.substr(0, mRootPath.find_last_of("\\/") + 1u);
+    mIOHandler = pImp->GetIOHandler();
 }
 }
 
 
 void EmbedTexturesProcess::Execute(aiScene* pScene) {
 void EmbedTexturesProcess::Execute(aiScene* pScene) {
-    if (pScene == nullptr || pScene->mRootNode == nullptr) return;
+    if (pScene == nullptr || pScene->mRootNode == nullptr || mIOHandler == nullptr){
+        return;
+    }
 
 
     aiString path;
     aiString path;
-
     uint32_t embeddedTexturesCount = 0u;
     uint32_t embeddedTexturesCount = 0u;
-
     for (auto matId = 0u; matId < pScene->mNumMaterials; ++matId) {
     for (auto matId = 0u; matId < pScene->mNumMaterials; ++matId) {
         auto material = pScene->mMaterials[matId];
         auto material = pScene->mMaterials[matId];
 
 
@@ -96,32 +100,36 @@ void EmbedTexturesProcess::Execute(aiScene* pScene) {
     ASSIMP_LOG_INFO("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." );
     ASSIMP_LOG_INFO("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." );
 }
 }
 
 
-bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const {
+bool EmbedTexturesProcess::addTexture(aiScene *pScene, const std::string &path) const {
     std::streampos imageSize = 0;
     std::streampos imageSize = 0;
     std::string    imagePath = path;
     std::string    imagePath = path;
 
 
     // Test path directly
     // Test path directly
-    std::ifstream file(imagePath, std::ios::binary | std::ios::ate);
-    if ((imageSize = file.tellg()) == std::streampos(-1)) {
+    if (!mIOHandler->Exists(imagePath)) {
         ASSIMP_LOG_WARN("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder.");
         ASSIMP_LOG_WARN("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder.");
 
 
         // Test path in root path
         // Test path in root path
         imagePath = mRootPath + path;
         imagePath = mRootPath + path;
-        file.open(imagePath, std::ios::binary | std::ios::ate);
-        if ((imageSize = file.tellg()) == std::streampos(-1)) {
+        if (!mIOHandler->Exists(imagePath)) {
             // Test path basename in root path
             // Test path basename in root path
             imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u);
             imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u);
-            file.open(imagePath, std::ios::binary | std::ios::ate);
-            if ((imageSize = file.tellg()) == std::streampos(-1)) {
+            if (!mIOHandler->Exists(imagePath)) {
                 ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, ".");
                 ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, ".");
                 return false;
                 return false;
             }
             }
         }
         }
     }
     }
+    IOStream* pFile = mIOHandler->Open(imagePath);
+    if (pFile == nullptr) {
+        ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, ".");
+        return false;
+    }
+    imageSize = pFile->FileSize();
 
 
     aiTexel* imageContent = new aiTexel[ 1ul + static_cast<unsigned long>( imageSize ) / sizeof(aiTexel)];
     aiTexel* imageContent = new aiTexel[ 1ul + static_cast<unsigned long>( imageSize ) / sizeof(aiTexel)];
-    file.seekg(0, std::ios::beg);
-    file.read(reinterpret_cast<char*>(imageContent), imageSize);
+    pFile->Seek(0, aiOrigin_SET);
+    pFile->Read(reinterpret_cast<char*>(imageContent), imageSize, 1);
+    mIOHandler->Close(pFile);
 
 
     // Enlarging the textures table
     // Enlarging the textures table
     unsigned int textureId = pScene->mNumTextures++;
     unsigned int textureId = pScene->mNumTextures++;

+ 4 - 1
code/PostProcessing/EmbedTexturesProcess.h

@@ -48,6 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 struct aiNode;
 struct aiNode;
 
 
+class IOSystem;
+
 namespace Assimp {
 namespace Assimp {
 
 
 /**
 /**
@@ -76,10 +78,11 @@ public:
 
 
 private:
 private:
     // Resolve the path and add the file content to the scene as a texture.
     // Resolve the path and add the file content to the scene as a texture.
-    bool addTexture(aiScene* pScene, std::string path) const;
+    bool addTexture(aiScene *pScene, const std::string &path) const;
 
 
 private:
 private:
     std::string mRootPath;
     std::string mRootPath;
+    IOSystem* mIOHandler = nullptr;
 };
 };
 
 
 } // namespace Assimp
 } // namespace Assimp

+ 0 - 1
contrib/draco/.ruby-version

@@ -1 +0,0 @@
-2.3.0

+ 0 - 31
contrib/draco/.travis.yml

@@ -1,31 +0,0 @@
-cache: ccache
-language: cpp
-matrix:
-  include:
-    - os: linux
-      dist: xenial
-      compiler: clang
-    - os: linux
-      dist: xenial
-      compiler: gcc
-    - os: osx
-      compiler: clang
-
-addons:
-  apt:
-    packages:
-    - cmake
-
-script:
-  # Output version info for compilers, cmake, and make
-  - ${CC} -v
-  - ${CXX} -v
-  - cmake --version
-  - make --version
-  # Clone googletest
-  - pushd .. && git clone https://github.com/google/googletest.git && popd
-  # Configure and build
-  - mkdir _travis_build && cd _travis_build
-  - cmake -G "Unix Makefiles" -DENABLE_TESTS=ON ..
-  - make -j10
-  - ./draco_tests

+ 1 - 7
contrib/draco/CMakeLists.txt

@@ -804,7 +804,7 @@ else()
       draco_points_enc)
       draco_points_enc)
 
 
   # Library targets that consume the object collections.
   # Library targets that consume the object collections.
-  if(MSVC OR WIN32)
+  if(MSVC)
     # In order to produce a DLL and import library the Windows tools require
     # In order to produce a DLL and import library the Windows tools require
     # that the exported symbols are part of the DLL target. The unfortunate side
     # that the exported symbols are part of the DLL target. The unfortunate side
     # effect of this is that a single configuration cannot output both the
     # effect of this is that a single configuration cannot output both the
@@ -889,9 +889,6 @@ else()
     # For Mac, we need to build a .bundle for the unity plugin.
     # For Mac, we need to build a .bundle for the unity plugin.
     if(APPLE)
     if(APPLE)
       set_target_properties(dracodec_unity PROPERTIES BUNDLE true)
       set_target_properties(dracodec_unity PROPERTIES BUNDLE true)
-    elseif(NOT unity_decoder_lib_type STREQUAL STATIC)
-      set_target_properties(dracodec_unity
-                            PROPERTIES SOVERSION ${DRACO_SOVERSION})
     endif()
     endif()
   endif()
   endif()
 
 
@@ -916,9 +913,6 @@ else()
     # For Mac, we need to build a .bundle for the plugin.
     # For Mac, we need to build a .bundle for the plugin.
     if(APPLE)
     if(APPLE)
       set_target_properties(draco_maya_wrapper PROPERTIES BUNDLE true)
       set_target_properties(draco_maya_wrapper PROPERTIES BUNDLE true)
-    else()
-      set_target_properties(draco_maya_wrapper
-                            PROPERTIES SOVERSION ${DRACO_SOVERSION})
     endif()
     endif()
   endif()
   endif()
 
 

+ 3 - 3
contrib/draco/README.md

@@ -2,16 +2,16 @@
 <img width="350px" src="docs/artwork/draco3d-vert.svg" />
 <img width="350px" src="docs/artwork/draco3d-vert.svg" />
 </p>
 </p>
 
 
-![Build Status: master](https://travis-ci.org/google/draco.svg?branch=master)
+[![Build Status](https://github.com/google/draco/workflows/Build/badge.svg)](https://github.com/google/draco/actions?query=workflow%3ABuild)
 
 
 News
 News
 =======
 =======
 ### Version 1.4.1 release
 ### Version 1.4.1 release
-* Using the versioned gstatic.com WASM and Javascript decoders is now
+* Using the versioned www.gstatic.com WASM and Javascript decoders is now
   recommended. To use v1.4.1, use this URL:
   recommended. To use v1.4.1, use this URL:
   * https://www.gstatic.com/draco/versioned/decoders/1.4.1/*
   * https://www.gstatic.com/draco/versioned/decoders/1.4.1/*
     * Replace the * with the files to load. E.g.
     * Replace the * with the files to load. E.g.
-    * https://gstatic.com/draco/versioned/decoders/1.4.1/draco_decoder.js
+    * https://www.gstatic.com/draco/versioned/decoders/1.4.1/draco_decoder.js
   * This works with the v1.3.6 and v1.4.0 releases, and will work with future
   * This works with the v1.3.6 and v1.4.0 releases, and will work with future
     Draco releases.
     Draco releases.
 * Bug fixes
 * Bug fixes

+ 8 - 1
contrib/draco/cmake/draco_build_definitions.cmake

@@ -6,7 +6,7 @@ set(DRACO_CMAKE_DRACO_BUILD_DEFINITIONS_CMAKE_ 1)
 # Utility for controlling the main draco library dependency. This changes in
 # Utility for controlling the main draco library dependency. This changes in
 # shared builds, and when an optional target requires a shared library build.
 # shared builds, and when an optional target requires a shared library build.
 macro(set_draco_target)
 macro(set_draco_target)
-  if(MSVC OR WIN32)
+  if(MSVC)
     set(draco_dependency draco)
     set(draco_dependency draco)
     set(draco_plugin_dependency ${draco_dependency})
     set(draco_plugin_dependency ${draco_dependency})
   else()
   else()
@@ -63,6 +63,11 @@ macro(draco_set_build_definitions)
     if(BUILD_SHARED_LIBS)
     if(BUILD_SHARED_LIBS)
       set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
       set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
     endif()
     endif()
+  else()
+    if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
+      # Ensure 64-bit platforms can support large files.
+      list(APPEND draco_defines "_LARGEFILE_SOURCE" "_FILE_OFFSET_BITS=64")
+    endif()
   endif()
   endif()
 
 
   if(ANDROID)
   if(ANDROID)
@@ -114,4 +119,6 @@ macro(draco_set_build_definitions)
     draco_check_emscripten_environment()
     draco_check_emscripten_environment()
     draco_get_required_emscripten_flags(FLAG_LIST_VAR draco_base_cxx_flags)
     draco_get_required_emscripten_flags(FLAG_LIST_VAR draco_base_cxx_flags)
   endif()
   endif()
+
+  draco_configure_sanitizer()
 endmacro()
 endmacro()

+ 0 - 63
contrib/draco/cmake/draco_features.cmake

@@ -1,63 +0,0 @@
-if(DRACO_CMAKE_DRACO_FEATURES_CMAKE_)
-  return()
-endif()
-set(DRACO_CMAKE_DRACO_FEATURES_CMAKE_ 1)
-
-set(draco_features_file_name "${draco_build_dir}/draco/draco_features.h")
-set(draco_features_list)
-
-# Macro that handles tracking of Draco preprocessor symbols for the purpose of
-# producing draco_features.h.
-#
-# draco_enable_feature(FEATURE <feature_name> [TARGETS <target_name>]) FEATURE
-# is required. It should be a Draco preprocessor symbol. TARGETS is optional. It
-# can be one or more draco targets.
-#
-# When the TARGETS argument is not present the preproc symbol is added to
-# draco_features.h. When it is draco_features.h is unchanged, and
-# target_compile_options() is called for each target specified.
-macro(draco_enable_feature)
-  set(def_flags)
-  set(def_single_arg_opts FEATURE)
-  set(def_multi_arg_opts TARGETS)
-  cmake_parse_arguments(DEF "${def_flags}" "${def_single_arg_opts}"
-                        "${def_multi_arg_opts}" ${ARGN})
-  if("${DEF_FEATURE}" STREQUAL "")
-    message(FATAL_ERROR "Empty FEATURE passed to draco_enable_feature().")
-  endif()
-
-  # Do nothing/return early if $DEF_FEATURE is already in the list.
-  list(FIND draco_features_list ${DEF_FEATURE} df_index)
-  if(NOT df_index EQUAL -1)
-    return()
-  endif()
-
-  list(LENGTH DEF_TARGETS df_targets_list_length)
-  if(${df_targets_list_length} EQUAL 0)
-    list(APPEND draco_features_list ${DEF_FEATURE})
-  else()
-    foreach(target ${DEF_TARGETS})
-      target_compile_definitions(${target} PRIVATE ${DEF_FEATURE})
-    endforeach()
-  endif()
-endmacro()
-
-# Function for generating draco_features.h.
-function(draco_generate_features_h)
-  file(WRITE "${draco_features_file_name}.new"
-       "// GENERATED FILE -- DO NOT EDIT\n\n" "#ifndef DRACO_FEATURES_H_\n"
-       "#define DRACO_FEATURES_H_\n\n")
-
-  foreach(feature ${draco_features_list})
-    file(APPEND "${draco_features_file_name}.new" "#define ${feature}\n")
-  endforeach()
-
-  file(APPEND "${draco_features_file_name}.new"
-       "\n#endif  // DRACO_FEATURES_H_")
-
-  # Will replace ${draco_features_file_name} only if the file content has
-  # changed. This prevents forced Draco rebuilds after CMake runs.
-  configure_file("${draco_features_file_name}.new"
-                 "${draco_features_file_name}")
-  file(REMOVE "${draco_features_file_name}.new")
-endfunction()

+ 9 - 0
contrib/draco/cmake/draco_flags.cmake

@@ -80,6 +80,12 @@ macro(draco_test_cxx_flag)
   # Run the actual compile test.
   # Run the actual compile test.
   unset(draco_all_cxx_flags_pass CACHE)
   unset(draco_all_cxx_flags_pass CACHE)
   message("--- Running combined CXX flags test, flags: ${all_cxx_flags}")
   message("--- Running combined CXX flags test, flags: ${all_cxx_flags}")
+
+  # check_cxx_compiler_flag() requires that the flags are a string. When flags
+  # are passed as a list it will remove the list separators, and attempt to run
+  # a compile command using list entries concatenated together as a single
+  # argument. Avoid the problem by forcing the argument to be a string.
+  draco_set_and_stringify(SOURCE_VARS all_cxx_flags DEST all_cxx_flags)
   check_cxx_compiler_flag("${all_cxx_flags}" draco_all_cxx_flags_pass)
   check_cxx_compiler_flag("${all_cxx_flags}" draco_all_cxx_flags_pass)
 
 
   if(cxx_test_FLAG_REQUIRED AND NOT draco_all_cxx_flags_pass)
   if(cxx_test_FLAG_REQUIRED AND NOT draco_all_cxx_flags_pass)
@@ -194,6 +200,9 @@ macro(draco_test_exe_linker_flag)
   else()
   else()
     unset(CMAKE_EXE_LINKER_FLAGS)
     unset(CMAKE_EXE_LINKER_FLAGS)
   endif()
   endif()
+
+  list(APPEND DRACO_EXE_LINKER_FLAGS ${${link_FLAG_LIST_VAR_NAME}})
+  list(REMOVE_DUPLICATES DRACO_EXE_LINKER_FLAGS)
 endmacro()
 endmacro()
 
 
 # Runs the draco compiler tests. This macro builds up the list of list var(s)
 # Runs the draco compiler tests. This macro builds up the list of list var(s)

+ 1 - 1
contrib/draco/cmake/draco_install.cmake

@@ -55,7 +55,7 @@ macro(draco_setup_install_target)
   install(TARGETS draco_encoder DESTINATION
   install(TARGETS draco_encoder DESTINATION
                   "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
                   "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
 
 
-  if(WIN32)
+  if(MSVC)
     install(TARGETS draco DESTINATION
     install(TARGETS draco DESTINATION
                     "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
                     "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
   else()
   else()

+ 10 - 10
contrib/draco/cmake/draco_sanitizer.cmake

@@ -5,28 +5,28 @@ set(DRACO_CMAKE_DRACO_SANITIZER_CMAKE_ 1)
 
 
 # Handles the details of enabling sanitizers.
 # Handles the details of enabling sanitizers.
 macro(draco_configure_sanitizer)
 macro(draco_configure_sanitizer)
-  if(DRACO_SANITIZE AND NOT MSVC)
+  if(DRACO_SANITIZE AND NOT EMSCRIPTEN AND NOT MSVC)
     if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
     if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
       if(DRACO_SANITIZE MATCHES "cfi")
       if(DRACO_SANITIZE MATCHES "cfi")
-        list(APPEND DRACO_CXX_FLAGS "-flto" "-fno-sanitize-trap=cfi")
-        list(APPEND DRACO_EXE_LINKER_FLAGS "-flto" "-fno-sanitize-trap=cfi"
+        list(APPEND SAN_CXX_FLAGS "-flto" "-fno-sanitize-trap=cfi")
+        list(APPEND SAN_LINKER_FLAGS "-flto" "-fno-sanitize-trap=cfi"
                     "-fuse-ld=gold")
                     "-fuse-ld=gold")
       endif()
       endif()
 
 
       if(${CMAKE_SIZEOF_VOID_P} EQUAL 4
       if(${CMAKE_SIZEOF_VOID_P} EQUAL 4
          AND DRACO_SANITIZE MATCHES "integer|undefined")
          AND DRACO_SANITIZE MATCHES "integer|undefined")
-        list(APPEND DRACO_EXE_LINKER_FLAGS "--rtlib=compiler-rt" "-lgcc_s")
+        list(APPEND SAN_LINKER_FLAGS "--rtlib=compiler-rt" "-lgcc_s")
       endif()
       endif()
     endif()
     endif()
 
 
-    list(APPEND DRACO_CXX_FLAGS "-fsanitize=${DRACO_SANITIZE}")
-    list(APPEND DRACO_EXE_LINKER_FLAGS "-fsanitize=${DRACO_SANITIZE}")
+    list(APPEND SAN_CXX_FLAGS "-fsanitize=${DRACO_SANITIZE}")
+    list(APPEND SAN_LINKER_FLAGS "-fsanitize=${DRACO_SANITIZE}")
 
 
     # Make sanitizer callstacks accurate.
     # Make sanitizer callstacks accurate.
-    list(APPEND DRACO_CXX_FLAGS "-fno-omit-frame-pointer"
-                "-fno-optimize-sibling-calls")
+    list(APPEND SAN_CXX_FLAGS "-fno-omit-frame-pointer")
+    list(APPEND SAN_CXX_FLAGS "-fno-optimize-sibling-calls")
 
 
-    draco_test_cxx_flag(FLAG_LIST_VAR_NAMES DRACO_CXX_FLAGS FLAG_REQUIRED)
-    draco_test_exe_linker_flag(FLAG_LIST_VAR_NAME DRACO_EXE_LINKER_FLAGS)
+    draco_test_cxx_flag(FLAG_LIST_VAR_NAMES SAN_CXX_FLAGS FLAG_REQUIRED)
+    draco_test_exe_linker_flag(FLAG_LIST_VAR_NAME SAN_LINKER_FLAGS)
   endif()
   endif()
 endmacro()
 endmacro()

+ 15 - 9
contrib/draco/cmake/draco_targets.cmake

@@ -87,6 +87,7 @@ macro(draco_add_executable)
   endif()
   endif()
 
 
   add_executable(${exe_NAME} ${exe_SOURCES})
   add_executable(${exe_NAME} ${exe_SOURCES})
+  set_target_properties(${exe_NAME} PROPERTIES VERSION ${DRACO_VERSION})
 
 
   if(exe_OUTPUT_NAME)
   if(exe_OUTPUT_NAME)
     set_target_properties(${exe_NAME} PROPERTIES OUTPUT_NAME ${exe_OUTPUT_NAME})
     set_target_properties(${exe_NAME} PROPERTIES OUTPUT_NAME ${exe_OUTPUT_NAME})
@@ -109,10 +110,11 @@ macro(draco_add_executable)
 
 
   if(exe_LINK_FLAGS OR DRACO_EXE_LINKER_FLAGS)
   if(exe_LINK_FLAGS OR DRACO_EXE_LINKER_FLAGS)
     if(${CMAKE_VERSION} VERSION_LESS "3.13")
     if(${CMAKE_VERSION} VERSION_LESS "3.13")
-      set(link_flags ${exe_LINK_FLAGS} ${DRACO_EXE_LINKER_FLAGS})
+      list(APPEND exe_LINK_FLAGS "${DRACO_EXE_LINKER_FLAGS}")
+      # LINK_FLAGS is managed as a string.
+      draco_set_and_stringify(SOURCE "${exe_LINK_FLAGS}" DEST exe_LINK_FLAGS)
       set_target_properties(${exe_NAME}
       set_target_properties(${exe_NAME}
-                            PROPERTIES LINK_FLAGS ${exe_LINK_FLAGS}
-                                       ${DRACO_EXE_LINKER_FLAGS})
+                            PROPERTIES LINK_FLAGS "${exe_LINK_FLAGS}")
     else()
     else()
       target_link_options(${exe_NAME} PRIVATE ${exe_LINK_FLAGS}
       target_link_options(${exe_NAME} PRIVATE ${exe_LINK_FLAGS}
                           ${DRACO_EXE_LINKER_FLAGS})
                           ${DRACO_EXE_LINKER_FLAGS})
@@ -130,7 +132,7 @@ macro(draco_add_executable)
   endif()
   endif()
 
 
   if(BUILD_SHARED_LIBS AND (MSVC OR WIN32))
   if(BUILD_SHARED_LIBS AND (MSVC OR WIN32))
-    target_compile_definitions(${lib_NAME} PRIVATE "DRACO_BUILDING_DLL=0")
+    target_compile_definitions(${exe_NAME} PRIVATE "DRACO_BUILDING_DLL=0")
   endif()
   endif()
 
 
   if(exe_LIB_DEPS)
   if(exe_LIB_DEPS)
@@ -163,8 +165,8 @@ endmacro()
 # cmake-format: off
 # cmake-format: off
 #   - OUTPUT_NAME: Override output file basename. Target basename defaults to
 #   - OUTPUT_NAME: Override output file basename. Target basename defaults to
 #     NAME. OUTPUT_NAME is ignored when BUILD_SHARED_LIBS is enabled and CMake
 #     NAME. OUTPUT_NAME is ignored when BUILD_SHARED_LIBS is enabled and CMake
-#     is generating a build for which MSVC or WIN32 are true. This is to avoid
-#     output basename collisions with DLL import libraries.
+#     is generating a build for which MSVC is true. This is to avoid output
+#     basename collisions with DLL import libraries.
 #   - TEST: Flag. Presence means treat library as a test.
 #   - TEST: Flag. Presence means treat library as a test.
 #   - DEFINES: List of preprocessor macro definitions.
 #   - DEFINES: List of preprocessor macro definitions.
 #   - INCLUDES: list of include directories for the target.
 #   - INCLUDES: list of include directories for the target.
@@ -259,7 +261,7 @@ macro(draco_add_library)
   endif()
   endif()
 
 
   if(lib_OUTPUT_NAME)
   if(lib_OUTPUT_NAME)
-    if(NOT (BUILD_SHARED_LIBS AND (MSVC OR WIN32)))
+    if(NOT (BUILD_SHARED_LIBS AND MSVC))
       set_target_properties(${lib_NAME}
       set_target_properties(${lib_NAME}
                             PROPERTIES OUTPUT_NAME ${lib_OUTPUT_NAME})
                             PROPERTIES OUTPUT_NAME ${lib_OUTPUT_NAME})
     endif()
     endif()
@@ -318,8 +320,12 @@ macro(draco_add_library)
     set_target_properties(${lib_NAME} PROPERTIES PREFIX "")
     set_target_properties(${lib_NAME} PROPERTIES PREFIX "")
   endif()
   endif()
 
 
-  if(lib_TYPE STREQUAL SHARED AND NOT MSVC)
-    set_target_properties(${lib_NAME} PROPERTIES SOVERSION ${DRACO_SOVERSION})
+  # VERSION and SOVERSION as necessary
+  if(NOT lib_TYPE STREQUAL STATIC AND NOT lib_TYPE STREQUAL MODULE)
+    set_target_properties(${lib_NAME} PROPERTIES VERSION ${DRACO_VERSION})
+    if(NOT MSVC)
+      set_target_properties(${lib_NAME} PROPERTIES SOVERSION ${DRACO_SOVERSION})
+    endif()
   endif()
   endif()
 
 
   if(BUILD_SHARED_LIBS AND (MSVC OR WIN32))
   if(BUILD_SHARED_LIBS AND (MSVC OR WIN32))

+ 7 - 7
contrib/draco/src/draco/core/cycle_timer.cc

@@ -17,31 +17,31 @@
 namespace draco {
 namespace draco {
 void DracoTimer::Start() {
 void DracoTimer::Start() {
 #ifdef _WIN32
 #ifdef _WIN32
-  QueryPerformanceCounter(&tv_start);
+  QueryPerformanceCounter(&tv_start_);
 #else
 #else
-  gettimeofday(&tv_start, nullptr);
+  gettimeofday(&tv_start_, nullptr);
 #endif
 #endif
 }
 }
 
 
 void DracoTimer::Stop() {
 void DracoTimer::Stop() {
 #ifdef _WIN32
 #ifdef _WIN32
-  QueryPerformanceCounter(&tv_end);
+  QueryPerformanceCounter(&tv_end_);
 #else
 #else
-  gettimeofday(&tv_end, nullptr);
+  gettimeofday(&tv_end_, nullptr);
 #endif
 #endif
 }
 }
 
 
 int64_t DracoTimer::GetInMs() {
 int64_t DracoTimer::GetInMs() {
 #ifdef _WIN32
 #ifdef _WIN32
   LARGE_INTEGER elapsed = {0};
   LARGE_INTEGER elapsed = {0};
-  elapsed.QuadPart = tv_end.QuadPart - tv_start.QuadPart;
+  elapsed.QuadPart = tv_end_.QuadPart - tv_start_.QuadPart;
 
 
   LARGE_INTEGER frequency = {0};
   LARGE_INTEGER frequency = {0};
   QueryPerformanceFrequency(&frequency);
   QueryPerformanceFrequency(&frequency);
   return elapsed.QuadPart * 1000 / frequency.QuadPart;
   return elapsed.QuadPart * 1000 / frequency.QuadPart;
 #else
 #else
-  const int64_t seconds = (tv_end.tv_sec - tv_start.tv_sec) * 1000;
-  const int64_t milliseconds = (tv_end.tv_usec - tv_start.tv_usec) / 1000;
+  const int64_t seconds = (tv_end_.tv_sec - tv_start_.tv_sec) * 1000;
+  const int64_t milliseconds = (tv_end_.tv_usec - tv_start_.tv_usec) / 1000;
   return seconds + milliseconds;
   return seconds + milliseconds;
 #endif
 #endif
 }
 }

+ 4 - 3
contrib/draco/src/draco/core/cycle_timer.h

@@ -20,9 +20,10 @@
 #define WIN32_LEAN_AND_MEAN
 #define WIN32_LEAN_AND_MEAN
 #endif
 #endif
 #include <windows.h>
 #include <windows.h>
-typedef LARGE_INTEGER timeval;
+typedef LARGE_INTEGER DracoTimeVal;
 #else
 #else
 #include <sys/time.h>
 #include <sys/time.h>
+typedef timeval DracoTimeVal;
 #endif
 #endif
 
 
 #include <cinttypes>
 #include <cinttypes>
@@ -39,8 +40,8 @@ class DracoTimer {
   int64_t GetInMs();
   int64_t GetInMs();
 
 
  private:
  private:
-  timeval tv_start;
-  timeval tv_end;
+  DracoTimeVal tv_start_;
+  DracoTimeVal tv_end_;
 };
 };
 
 
 typedef DracoTimer CycleTimer;
 typedef DracoTimer CycleTimer;

+ 2 - 1
contrib/draco/src/draco/io/parser_utils.cc

@@ -18,6 +18,7 @@
 #include <cctype>
 #include <cctype>
 #include <cmath>
 #include <cmath>
 #include <iterator>
 #include <iterator>
+#include <limits>
 
 
 namespace draco {
 namespace draco {
 namespace parser {
 namespace parser {
@@ -252,7 +253,7 @@ DecoderBuffer ParseLineIntoDecoderBuffer(DecoderBuffer *buffer) {
 
 
 std::string ToLower(const std::string &str) {
 std::string ToLower(const std::string &str) {
   std::string out;
   std::string out;
-  std::transform(str.begin(), str.end(), std::back_inserter(out), [](unsigned char c){return tolower(c);});
+  std::transform(str.begin(), str.end(), std::back_inserter(out), tolower);
   return out;
   return out;
 }
 }
 
 

+ 2 - 2
contrib/draco/src/draco/io/ply_reader.cc

@@ -268,14 +268,14 @@ std::vector<std::string> PlyReader::SplitWords(const std::string &line) {
   while ((end = line.find_first_of(" \t\n\v\f\r", start)) !=
   while ((end = line.find_first_of(" \t\n\v\f\r", start)) !=
          std::string::npos) {
          std::string::npos) {
     const std::string word(line.substr(start, end - start));
     const std::string word(line.substr(start, end - start));
-    if (!std::all_of(word.begin(), word.end(), [](unsigned char c){return isspace(c);})) {
+    if (!std::all_of(word.begin(), word.end(), isspace)) {
       output.push_back(word);
       output.push_back(word);
     }
     }
     start = end + 1;
     start = end + 1;
   }
   }
 
 
   const std::string last_word(line.substr(start));
   const std::string last_word(line.substr(start));
-  if (!std::all_of(last_word.begin(), last_word.end(), [](unsigned char c){return isspace(c);})) {
+  if (!std::all_of(last_word.begin(), last_word.end(), isspace)) {
     output.push_back(last_word);
     output.push_back(last_word);
   }
   }
   return output;
   return output;

+ 7 - 0
contrib/draco/src/draco/io/stdio_file_reader.cc

@@ -87,7 +87,14 @@ size_t StdioFileReader::GetFileSize() {
     return false;
     return false;
   }
   }
 
 
+#if _FILE_OFFSET_BITS == 64
+  const size_t file_size = static_cast<size_t>(ftello(file_));
+#elif defined _WIN64
+  const size_t file_size = static_cast<size_t>(_ftelli64(file_));
+#else
   const size_t file_size = static_cast<size_t>(ftell(file_));
   const size_t file_size = static_cast<size_t>(ftell(file_));
+#endif
+
   rewind(file_);
   rewind(file_);
 
 
   return file_size;
   return file_size;

+ 1 - 1
contrib/poly2tri/poly2tri/sweep/sweep.cc

@@ -129,7 +129,7 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl
       EdgeEvent( tcx, ep, *p1, triangle, *p1 );
       EdgeEvent( tcx, ep, *p1, triangle, *p1 );
     } else {
     } else {
 	  // ASSIMP_CHANGE (aramis_acg)
 	  // ASSIMP_CHANGE (aramis_acg)
-      std::runtime_error("EdgeEvent - collinear points not supported");
+      throw std::runtime_error("EdgeEvent - collinear points not supported");
     }
     }
     return;
     return;
   }
   }

+ 105 - 23
include/assimp/material.h

@@ -144,7 +144,7 @@ enum aiTextureMapMode {
 enum aiTextureMapping {
 enum aiTextureMapping {
     /** The mapping coordinates are taken from an UV channel.
     /** The mapping coordinates are taken from an UV channel.
      *
      *
-     *  The #AI_MATKEY_UVWSRC key specifies from which UV channel
+     *  #AI_MATKEY_UVWSRC property specifies from which UV channel
      *  the texture coordinates are to be taken from (remember,
      *  the texture coordinates are to be taken from (remember,
      *  meshes can have more than one UV channel).
      *  meshes can have more than one UV channel).
     */
     */
@@ -202,11 +202,15 @@ enum aiTextureType {
 
 
     /** The texture is combined with the result of the diffuse
     /** The texture is combined with the result of the diffuse
      *  lighting equation.
      *  lighting equation.
+     *  OR
+     *  PBR Specular/Glossiness
      */
      */
     aiTextureType_DIFFUSE = 1,
     aiTextureType_DIFFUSE = 1,
 
 
     /** The texture is combined with the result of the specular
     /** The texture is combined with the result of the specular
      *  lighting equation.
      *  lighting equation.
+     *  OR
+     *  PBR Specular/Glossiness
      */
      */
     aiTextureType_SPECULAR = 2,
     aiTextureType_SPECULAR = 2,
 
 
@@ -288,6 +292,32 @@ enum aiTextureType {
     aiTextureType_DIFFUSE_ROUGHNESS = 16,
     aiTextureType_DIFFUSE_ROUGHNESS = 16,
     aiTextureType_AMBIENT_OCCLUSION = 17,
     aiTextureType_AMBIENT_OCCLUSION = 17,
 
 
+    /** PBR Material Modifiers
+    * Some modern renderers have further PBR modifiers that may be overlaid
+    * on top of the 'base' PBR materials for additional realism.
+    * These use multiple texture maps, so only the base type is directly defined
+    */
+
+    /** Sheen
+    * Generally used to simulate textiles that are covered in a layer of microfibers
+    * eg velvet
+    * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_sheen
+    */
+    aiTextureType_SHEEN = 19,
+
+    /** Clearcoat
+    * Simulates a layer of 'polish' or 'laquer' layered on top of a PBR substrate
+    * https://autodesk.github.io/standard-surface/#closures/coating
+    * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat
+    */
+    aiTextureType_CLEARCOAT = 20,
+
+    /** Transmission
+    * Simulates transmission through the surface
+    * May include further information such as wall thickness
+    */
+    aiTextureType_TRANSMISSION = 21,
+
     /** Unknown texture
     /** Unknown texture
      *
      *
      *  A texture reference that does not match any of the definitions
      *  A texture reference that does not match any of the definitions
@@ -309,7 +339,9 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in);
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** @brief Defines all shading models supported by the library
 /** @brief Defines all shading models supported by the library
- *
+ * 
+ *  Property: #AI_MATKEY_SHADING_MODEL
+ * 
  *  The list of shading modes has been taken from Blender.
  *  The list of shading modes has been taken from Blender.
  *  See Blender documentation for more information. The API does
  *  See Blender documentation for more information. The API does
  *  not distinguish between "specular" and "diffuse" shaders (thus the
  *  not distinguish between "specular" and "diffuse" shaders (thus the
@@ -318,6 +350,7 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in);
  *  Again, this value is just a hint. Assimp tries to select the shader whose
  *  Again, this value is just a hint. Assimp tries to select the shader whose
  *  most common implementation matches the original rendering results of the
  *  most common implementation matches the original rendering results of the
  *  3D modeler which wrote a particular model as closely as possible.
  *  3D modeler which wrote a particular model as closely as possible.
+ *
  */
  */
 enum aiShadingMode {
 enum aiShadingMode {
     /** Flat shading. Shading is done on per-face base,
     /** Flat shading. Shading is done on per-face base,
@@ -364,13 +397,28 @@ enum aiShadingMode {
     aiShadingMode_CookTorrance = 0x8,
     aiShadingMode_CookTorrance = 0x8,
 
 
     /** No shading at all. Constant light influence of 1.0.
     /** No shading at all. Constant light influence of 1.0.
+    * Also known as "Unlit"
     */
     */
     aiShadingMode_NoShading = 0x9,
     aiShadingMode_NoShading = 0x9,
+    aiShadingMode_Unlit = aiShadingMode_NoShading, // Alias
 
 
     /** Fresnel shading
     /** Fresnel shading
      */
      */
     aiShadingMode_Fresnel = 0xa,
     aiShadingMode_Fresnel = 0xa,
 
 
+    /** Physically-Based Rendering (PBR) shading using
+    * Bidirectional scattering/reflectance distribution function (BSDF/BRDF)
+    * There are multiple methods under this banner, and model files may provide
+    * data for more than one PBR-BRDF method.
+    * Applications should use the set of provided properties to determine which
+    * of their preferred PBR rendering methods are likely to be available
+    * eg:
+    * - If AI_MATKEY_METALLIC_FACTOR is set, then a Metallic/Roughness is available
+    * - If AI_MATKEY_GLOSSINESS_FACTOR is set, then a Specular/Glossiness is available
+    * Note that some PBR methods allow layering of techniques
+    */
+    aiShadingMode_PBR_BRDF = 0xb,
+
 #ifndef SWIG
 #ifndef SWIG
     _aiShadingMode_Force32Bit = INT_MAX
     _aiShadingMode_Force32Bit = INT_MAX
 #endif
 #endif
@@ -922,12 +970,66 @@ extern "C" {
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 // PBR material support
 // PBR material support
+// --------------------
+// Properties defining PBR rendering techniques
 #define AI_MATKEY_USE_COLOR_MAP "$mat.useColorMap", 0, 0
 #define AI_MATKEY_USE_COLOR_MAP "$mat.useColorMap", 0, 0
+
+// Metallic/Roughness Workflow
+// ---------------------------
+// Base RGBA color factor. Will be multiplied by final base color texture values if extant
+// Note: Importers may choose to copy this into AI_MATKEY_COLOR_DIFFUSE for compatibility
+// with renderers and formats that do not support Metallic/Roughness PBR
 #define AI_MATKEY_BASE_COLOR "$clr.base", 0, 0
 #define AI_MATKEY_BASE_COLOR "$clr.base", 0, 0
+#define AI_MATKEY_BASE_COLOR_TEXTURE aiTextureType_BASE_COLOR, 0
 #define AI_MATKEY_USE_METALLIC_MAP "$mat.useMetallicMap", 0, 0
 #define AI_MATKEY_USE_METALLIC_MAP "$mat.useMetallicMap", 0, 0
+// Metallic factor. 0.0 = Full Dielectric, 1.0 = Full Metal
 #define AI_MATKEY_METALLIC_FACTOR "$mat.metallicFactor", 0, 0
 #define AI_MATKEY_METALLIC_FACTOR "$mat.metallicFactor", 0, 0
+#define AI_MATKEY_METALLIC_TEXTURE aiTextureType_METALNESS, 0
 #define AI_MATKEY_USE_ROUGHNESS_MAP "$mat.useRoughnessMap", 0, 0
 #define AI_MATKEY_USE_ROUGHNESS_MAP "$mat.useRoughnessMap", 0, 0
+// Roughness factor. 0.0 = Perfectly Smooth, 1.0 = Completely Rough
 #define AI_MATKEY_ROUGHNESS_FACTOR "$mat.roughnessFactor", 0, 0
 #define AI_MATKEY_ROUGHNESS_FACTOR "$mat.roughnessFactor", 0, 0
+#define AI_MATKEY_ROUGHNESS_TEXTURE aiTextureType_DIFFUSE_ROUGHNESS, 0
+
+// Specular/Glossiness Workflow
+// ---------------------------
+// Diffuse/Albedo Color. Note: Pure Metals have a diffuse of {0,0,0}
+// AI_MATKEY_COLOR_DIFFUSE
+// Specular Color.
+// Note: Metallic/Roughness may also have a Specular Color
+// AI_MATKEY_COLOR_SPECULAR
+#define AI_MATKEY_SPECULAR_FACTOR "$mat.specularFactor", 0, 0
+// Glossiness factor. 0.0 = Completely Rough, 1.0 = Perfectly Smooth
+#define AI_MATKEY_GLOSSINESS_FACTOR "$mat.glossinessFactor", 0, 0
+
+// Sheen
+// -----
+// Sheen base RGB color. Default {0,0,0}
+#define AI_MATKEY_SHEEN_COLOR_FACTOR "$clr.sheen.factor", 0, 0
+// Sheen Roughness Factor.
+#define AI_MATKEY_SHEEN_ROUGHNESS_FACTOR "$mat.sheen.roughnessFactor", 0, 0
+#define AI_MATKEY_SHEEN_COLOR_TEXTURE aiTextureType_SHEEN, 0
+#define AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE aiTextureType_SHEEN, 1
+
+// Clearcoat
+// ---------
+// Clearcoat layer intensity. 0.0 = none (disabled)
+#define AI_MATKEY_CLEARCOAT_FACTOR "$mat.clearcoat.factor", 0, 0
+#define AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR "$mat.clearcoat.roughnessFactor", 0, 0
+#define AI_MATKEY_CLEARCOAT_TEXTURE aiTextureType_CLEARCOAT, 0
+#define AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_CLEARCOAT, 1
+#define AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE aiTextureType_CLEARCOAT, 2
+
+// Transmission
+// ------------
+// https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission
+// Base percentage of light transmitted through the surface. 0.0 = Opaque, 1.0 = Fully transparent
+#define AI_MATKEY_TRANSMISSION_FACTOR "$mat.transmission.factor", 0, 0
+// Texture defining percentage of light transmitted through the surface.
+// Multiplied by AI_MATKEY_TRANSMISSION_FACTOR
+#define AI_MATKEY_TRANSMISSION_TEXTURE aiTextureType_TRANSMISSION, 0
+
+// Emissive
+// --------
 #define AI_MATKEY_USE_EMISSIVE_MAP "$mat.useEmissiveMap", 0, 0
 #define AI_MATKEY_USE_EMISSIVE_MAP "$mat.useEmissiveMap", 0, 0
 #define AI_MATKEY_EMISSIVE_INTENSITY "$mat.emissiveIntensity", 0, 0
 #define AI_MATKEY_EMISSIVE_INTENSITY "$mat.emissiveIntensity", 0, 0
 #define AI_MATKEY_USE_AO_MAP "$mat.useAOMap", 0, 0
 #define AI_MATKEY_USE_AO_MAP "$mat.useAOMap", 0, 0
@@ -1397,8 +1499,6 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray(
         ai_real *pOut,
         ai_real *pOut,
         unsigned int *pMax);
         unsigned int *pMax);
 
 
-#ifdef __cplusplus
-
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** @brief Retrieve a single float property with a specific key from the material.
 /** @brief Retrieve a single float property with a specific key from the material.
 *
 *
@@ -1418,7 +1518,7 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray(
 * @return Specifies whether the key has been found. If not, the output
 * @return Specifies whether the key has been found. If not, the output
 *   float remains unmodified.*/
 *   float remains unmodified.*/
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
-inline aiReturn aiGetMaterialFloat(const aiMaterial *pMat,
+inline aiReturn aiGetMaterialFloat(const C_STRUCT aiMaterial *pMat,
         const char *pKey,
         const char *pKey,
         unsigned int type,
         unsigned int type,
         unsigned int index,
         unsigned int index,
@@ -1426,14 +1526,6 @@ inline aiReturn aiGetMaterialFloat(const aiMaterial *pMat,
     return aiGetMaterialFloatArray(pMat, pKey, type, index, pOut, (unsigned int *)0x0);
     return aiGetMaterialFloatArray(pMat, pKey, type, index, pOut, (unsigned int *)0x0);
 }
 }
 
 
-#else
-
-// Use our friend, the C preprocessor
-#define aiGetMaterialFloat (pMat, type, index, pKey, pOut) \
-        aiGetMaterialFloatArray(pMat, type, index, pKey, pOut, NULL)
-
-#endif //!__cplusplus
-
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** @brief Retrieve an array of integer values with a specific key
 /** @brief Retrieve an array of integer values with a specific key
  *  from a material
  *  from a material
@@ -1446,8 +1538,6 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialIntegerArray(const C_STRUCT aiMaterial *
         int *pOut,
         int *pOut,
         unsigned int *pMax);
         unsigned int *pMax);
 
 
-#ifdef __cplusplus
-
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** @brief Retrieve an integer property with a specific key from a material
 /** @brief Retrieve an integer property with a specific key from a material
  *
  *
@@ -1461,14 +1551,6 @@ inline aiReturn aiGetMaterialInteger(const C_STRUCT aiMaterial *pMat,
     return aiGetMaterialIntegerArray(pMat, pKey, type, index, pOut, (unsigned int *)0x0);
     return aiGetMaterialIntegerArray(pMat, pKey, type, index, pOut, (unsigned int *)0x0);
 }
 }
 
 
-#else
-
-// use our friend, the C preprocessor
-#define aiGetMaterialInteger (pMat, type, index, pKey, pOut) \
-        aiGetMaterialIntegerArray(pMat, type, index, pKey, pOut, NULL)
-
-#endif //!__cplusplus
-
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** @brief Retrieve a color value from the material property table
 /** @brief Retrieve a color value from the material property table
 *
 *

+ 1 - 1
include/assimp/metadata.h

@@ -432,7 +432,7 @@ struct aiMetadata {
 
 
     /// Check whether there is a metadata entry for the given key.
     /// Check whether there is a metadata entry for the given key.
     /// \param [in] Key - the key value value to check for.
     /// \param [in] Key - the key value value to check for.
-    inline bool HasKey(const char *key) {
+    inline bool HasKey(const char *key) const {
         if (nullptr == key) {
         if (nullptr == key) {
             return false;
             return false;
         }
         }

+ 23 - 23
include/assimp/pbrmaterial.h

@@ -50,32 +50,32 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #   pragma GCC system_header
 #   pragma GCC system_header
 #endif
 #endif
 
 
-#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0
-#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0
-#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0
-#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1
+//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0
+//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0
+//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0
+//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1
 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0
 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0
 #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0
 #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0
 #define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0
 #define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0
-#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0
-#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0
-#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0
-#define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0
-#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0
-#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0
-#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE aiTextureType_UNKNOWN, 1
-#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 2
-#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT "$mat.gltf.materialClearcoat", 0, 0
-#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR "$mat.gltf.materialClearcoat.clearcoatFactor", 0, 0
-#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR "$mat.gltf.materialClearcoat.clearcoatRoughnessFactor", 0, 0
-#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE aiTextureType_UNKNOWN, 3
-#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 4
-#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE aiTextureType_NORMALS, 1
-#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION "$mat.gltf.materialTransmission", 0, 0
-#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR "$mat.gltf.materialTransmission.transmissionFactor", 0, 0
-#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE aiTextureType_UNKNOWN, 5
+//#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0
+//#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0
+//#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0
+//#define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0
+//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0
+//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0
+//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE aiTextureType_UNKNOWN, 1
+//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 2
+//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT "$mat.gltf.materialClearcoat", 0, 0
+//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR "$mat.gltf.materialClearcoat.clearcoatFactor", 0, 0
+//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR "$mat.gltf.materialClearcoat.clearcoatRoughnessFactor", 0, 0
+//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE aiTextureType_UNKNOWN, 3
+//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 4
+//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE aiTextureType_NORMALS, 1
+//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION "$mat.gltf.materialTransmission", 0, 0
+//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR "$mat.gltf.materialTransmission.transmissionFactor", 0, 0
+//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE aiTextureType_UNKNOWN, 5
 
 
-#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord"
+//#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord"
 #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname"
 #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname"
 #define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid"
 #define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid"
 #define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag"
 #define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag"
@@ -83,7 +83,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define _AI_MATKEY_GLTF_SCALE_BASE "$tex.scale"
 #define _AI_MATKEY_GLTF_SCALE_BASE "$tex.scale"
 #define _AI_MATKEY_GLTF_STRENGTH_BASE "$tex.strength"
 #define _AI_MATKEY_GLTF_STRENGTH_BASE "$tex.strength"
 
 
-#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD(type, N) _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N
+//#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD(type, N) _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N
 #define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N
 #define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N
 #define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N
 #define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N
 #define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N
 #define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N

+ 18 - 7
include/assimp/scene.h

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 
 Copyright (c) 2006-2021, assimp team
 Copyright (c) 2006-2021, assimp team
 
 
-
-
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,
@@ -397,22 +395,35 @@ struct aiScene
 
 
     //! Returns an embedded texture
     //! Returns an embedded texture
     const aiTexture* GetEmbeddedTexture(const char* filename) const {
     const aiTexture* GetEmbeddedTexture(const char* filename) const {
+        return GetEmbeddedTextureAndIndex(filename).first;
+    }
+
+    //! Returns an embedded texture and its index
+    std::pair<const aiTexture*, int> GetEmbeddedTextureAndIndex(const char* filename) const {
+        if(nullptr==filename) {
+            return std::make_pair(nullptr, -1);
+        }
         // lookup using texture ID (if referenced like: "*1", "*2", etc.)
         // lookup using texture ID (if referenced like: "*1", "*2", etc.)
         if ('*' == *filename) {
         if ('*' == *filename) {
             int index = std::atoi(filename + 1);
             int index = std::atoi(filename + 1);
-            if (0 > index || mNumTextures <= static_cast<unsigned>(index))
-                return nullptr;
-            return mTextures[index];
+            if (0 > index || mNumTextures <= static_cast<unsigned>(index)) {
+                return std::make_pair(nullptr, -1);
+            }
+            return std::make_pair(mTextures[index], index);
         }
         }
         // lookup using filename
         // lookup using filename
         const char* shortFilename = GetShortFilename(filename);
         const char* shortFilename = GetShortFilename(filename);
+        if (nullptr == shortFilename) {
+            return std::make_pair(nullptr, -1);
+        }
+
         for (unsigned int i = 0; i < mNumTextures; i++) {
         for (unsigned int i = 0; i < mNumTextures; i++) {
             const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str());
             const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str());
             if (strcmp(shortTextureFilename, shortFilename) == 0) {
             if (strcmp(shortTextureFilename, shortFilename) == 0) {
-                return mTextures[i];
+                return std::make_pair(mTextures[i], i);
             }
             }
         }
         }
-        return nullptr;
+        return std::make_pair(nullptr, -1);
     }
     }
 #endif // __cplusplus
 #endif // __cplusplus
 
 

BIN
test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png


BIN
test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin


+ 1669 - 0
test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf

@@ -0,0 +1,1669 @@
+{
+    "asset" : {
+        "generator" : "Khronos glTF Blender I/O v1.2.8 with hand-edits for clearcoat",
+        "version" : "2.0"
+    },
+    "extensionsUsed": [
+        "KHR_materials_clearcoat"
+    ],
+    "scene" : 0,
+    "scenes" : [
+        {
+            "name" : "Scene",
+            "nodes" : [
+                3,
+                7,
+                11,
+                15,
+                19,
+                23,
+                24,
+                25,
+                26,
+                27,
+                28,
+                29,
+                30,
+                31,
+                32
+            ]
+        }
+    ],
+    "nodes" : [
+        {
+            "mesh" : 0,
+            "name" : "BaseLayerSample",
+            "translation" : [
+                -2.0999999046325684,
+                0,
+                0
+            ]
+        },
+        {
+            "mesh" : 1,
+            "name" : "ClearCoatSample"
+        },
+        {
+            "mesh" : 2,
+            "name" : "CoatOnlySample",
+            "translation" : [
+                2.0999999046325684,
+                0,
+                0
+            ]
+        },
+        {
+            "children" : [
+                0,
+                1,
+                2
+            ],
+            "name" : "R0_SimpleCoatTest",
+            "translation" : [
+                0,
+                5.25,
+                0
+            ]
+        },
+        {
+            "mesh" : 3,
+            "name" : "R1_BaseLayerSample",
+            "translation" : [
+                -2.0999999046325684,
+                0,
+                0
+            ]
+        },
+        {
+            "mesh" : 4,
+            "name" : "R1_ClearCoatSample"
+        },
+        {
+            "mesh" : 5,
+            "name" : "R1_CoatOnlySample",
+            "translation" : [
+                2.0999999046325684,
+                0,
+                0
+            ]
+        },
+        {
+            "children" : [
+                4,
+                5,
+                6
+            ],
+            "name" : "R1_PartialCoatTest",
+            "translation" : [
+                0,
+                3.1500000953674316,
+                0
+            ]
+        },
+        {
+            "mesh" : 6,
+            "name" : "R2_BaseLayerSample",
+            "translation" : [
+                -2.0999999046325684,
+                0,
+                0
+            ]
+        },
+        {
+            "mesh" : 7,
+            "name" : "R2_ClearCoatSample"
+        },
+        {
+            "mesh" : 8,
+            "name" : "R2_CoatOnlySample",
+            "translation" : [
+                2.0999999046325684,
+                0,
+                0
+            ]
+        },
+        {
+            "children" : [
+                8,
+                9,
+                10
+            ],
+            "name" : "R2_RoughnessVariations",
+            "translation" : [
+                0,
+                1.0499999523162842,
+                0
+            ]
+        },
+        {
+            "mesh" : 9,
+            "name" : "R3_BaseLayerSample",
+            "translation" : [
+                -2.0999999046325684,
+                0,
+                0
+            ]
+        },
+        {
+            "mesh" : 10,
+            "name" : "R3_ClearCoatSample"
+        },
+        {
+            "mesh" : 11,
+            "name" : "R3_CoatOnlySample",
+            "translation" : [
+                2.0999999046325684,
+                0,
+                0
+            ]
+        },
+        {
+            "children" : [
+                12,
+                13,
+                14
+            ],
+            "name" : "R3_BaseNormals",
+            "translation" : [
+                0,
+                -1.0499999523162842,
+                0
+            ]
+        },
+        {
+            "mesh" : 12,
+            "name" : "R4_BaseLayerSample",
+            "translation" : [
+                -2.0999999046325684,
+                0,
+                0
+            ]
+        },
+        {
+            "mesh" : 13,
+            "name" : "R4_ClearCoatSample"
+        },
+        {
+            "mesh" : 14,
+            "name" : "R4_CoatOnlySample",
+            "translation" : [
+                2.0999999046325684,
+                0,
+                0
+            ]
+        },
+        {
+            "children" : [
+                16,
+                17,
+                18
+            ],
+            "name" : "R4_CoatNormals",
+            "translation" : [
+                0,
+                -5.25,
+                0
+            ]
+        },
+        {
+            "mesh" : 15,
+            "name" : "R5_BaseLayerSample",
+            "translation" : [
+                -2.0999999046325684,
+                0,
+                0
+            ]
+        },
+        {
+            "mesh" : 16,
+            "name" : "R5_ClearCoatSample"
+        },
+        {
+            "mesh" : 17,
+            "name" : "R5_CoatOnlySample",
+            "translation" : [
+                2.0999999046325684,
+                0,
+                0
+            ]
+        },
+        {
+            "children" : [
+                20,
+                21,
+                22
+            ],
+            "name" : "R5_SharedNormals",
+            "translation" : [
+                0,
+                -3.1500000953674316,
+                0
+            ]
+        },
+        {
+            "mesh" : 18,
+            "name" : "X2_Label_CoatingOnly",
+            "translation" : [
+                2.0712804794311523,
+                6.619500160217285,
+                0
+            ]
+        },
+        {
+            "mesh" : 19,
+            "name" : "Y0_Label_SimpleCoating",
+            "translation" : [
+                -5.3578033447265625,
+                5.25,
+                0
+            ]
+        },
+        {
+            "mesh" : 20,
+            "name" : "Y1_Label_PartialCoating",
+            "translation" : [
+                -5.3578033447265625,
+                3.1673200130462646,
+                0
+            ]
+        },
+        {
+            "mesh" : 21,
+            "name" : "Y2_Label_Roughness",
+            "translation" : [
+                -5.3578033447265625,
+                1.1383899450302124,
+                0
+            ]
+        },
+        {
+            "mesh" : 22,
+            "name" : "Y3_Label_BaseNormals",
+            "translation" : [
+                -5.3578033447265625,
+                -1.099429965019226,
+                0
+            ]
+        },
+        {
+            "mesh" : 23,
+            "name" : "Y4_Label_CoatNormals",
+            "translation" : [
+                -5.3578033447265625,
+                -5.252500057220459,
+                0
+            ]
+        },
+        {
+            "mesh" : 24,
+            "name" : "Y5_Label_SharedNormals",
+            "translation" : [
+                -5.3578033447265625,
+                -3.1963000297546387,
+                0
+            ]
+        },
+        {
+            "mesh" : 25,
+            "name" : "X0_Label_BaseLayer",
+            "translation" : [
+                -2.087031602859497,
+                6.616230010986328,
+                0
+            ]
+        },
+        {
+            "mesh" : 26,
+            "name" : "X1_Label_Coated",
+            "translation" : [
+                0,
+                6.614150047302246,
+                0
+            ]
+        }
+    ],
+    "materials" : [
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "Simple_Base",
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0.5,
+                    0.019999999552965164,
+                    0.009999999776482582,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.4399999976158142
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "Simple_Coated",
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0.5,
+                    0.019999999552965164,
+                    0.009999999776482582,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.4399999976158142
+            },
+            "extensions": {
+                "KHR_materials_clearcoat": {
+                    "clearcoatFactor": 1,
+                    "clearcoatRoughnessFactor": 0.03
+                }
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "Simple_Coating",
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0,
+                    0,
+                    0,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.029999999329447746
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "Partial_Base",
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0.012983020395040512,
+                    0.022173883393406868,
+                    0.10946174710988998,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.4399999976158142
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "Partial_Coated",
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0.012983020395040512,
+                    0.022173883393406868,
+                    0.10946174710988998,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.4399999976158142
+            },
+            "extensions": {
+                "KHR_materials_clearcoat": {
+                    "clearcoatFactor": 1,
+                    "clearcoatRoughnessFactor": 0.03,
+                    "clearcoatTexture": {
+                        "index": 0,
+                        "texCoord": 0
+                    }
+                }
+            }
+        },
+        {
+            "alphaMode" : "BLEND",
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "Partial_Coating",
+            "pbrMetallicRoughness" : {
+                "baseColorTexture" : {
+                    "index" : 1,
+                    "texCoord" : 0
+                },
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.029999999329447746
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "RoughVariations_Base",
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0.012983020395040512,
+                    0.022173883393406868,
+                    0.10946174710988998,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.6000000238418579
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "RoughVariations_Coated",
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0.012983020395040512,
+                    0.022173883393406868,
+                    0.10946174710988998,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.6000000238418579
+            },
+            "extensions": {
+                "KHR_materials_clearcoat": {
+                    "clearcoatFactor": 1,
+                    "clearcoatRoughnessFactor": 1,
+                    "clearcoatRoughnessTexture": {
+                        "index": 2,
+                        "texCoord": 0
+                    }
+                }
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "RoughVariations_Coating",
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0,
+                    0,
+                    0,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "metallicRoughnessTexture" : {
+                    "index" : 2,
+                    "texCoord" : 0
+                }
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "BaseNorm_Base",
+            "normalTexture" : {
+                "index" : 3,
+                "texCoord" : 0
+            },
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0.012983020395040512,
+                    0.022173883393406868,
+                    0.10946174710988998,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.4399999976158142
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "BaseNorm_Coated",
+            "normalTexture" : {
+                "index" : 4,
+                "texCoord" : 0
+            },
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0.012848637998104095,
+                    0.021861059591174126,
+                    0.11068868637084961,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.4399999976158142
+            },
+            "extensions": {
+                "KHR_materials_clearcoat": {
+                    "clearcoatFactor": 1,
+                    "clearcoatRoughnessFactor": 0.03
+                }
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "BaseNorm_Coating",
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0,
+                    0,
+                    0,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.029999999329447746
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "CoatNorm_Base",
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0.012983020395040512,
+                    0.022173883393406868,
+                    0.10946174710988998,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.4399999976158142
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "CoatNorm_Coated",
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0.012983020395040512,
+                    0.022173883393406868,
+                    0.10946174710988998,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.4399999976158142
+            },
+            "extensions": {
+                "KHR_materials_clearcoat": {
+                    "clearcoatFactor": 1,
+                    "clearcoatRoughnessFactor": 0.03,
+                    "clearcoatNormalTexture": {
+                        "index": 5,
+                        "texCoord": 0,
+                        "scale": 1
+                    }
+                }
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "CoatNorm_Coating",
+            "normalTexture" : {
+                "index" : 5,
+                "texCoord" : 0
+            },
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0,
+                    0,
+                    0,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.029999999329447746
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "SharedNorm_Base",
+            "normalTexture" : {
+                "index" : 6,
+                "texCoord" : 0
+            },
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0.012983020395040512,
+                    0.022173883393406868,
+                    0.10946174710988998,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.4399999976158142
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "SharedNorm_Coated",
+            "normalTexture" : {
+                "index" : 7,
+                "texCoord" : 0
+            },
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0.012983020395040512,
+                    0.022173883393406868,
+                    0.10946174710988998,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.4399999976158142
+            },
+            "extensions": {
+                "KHR_materials_clearcoat": {
+                    "clearcoatFactor": 1,
+                    "clearcoatRoughnessFactor": 0.03,
+                    "clearcoatNormalTexture": {
+                        "index": 7,
+                        "texCoord": 0
+                    }
+                }
+            }
+        },
+        {
+            "emissiveFactor" : [
+                0,
+                0,
+                0
+            ],
+            "name" : "SharedNorm_Coating",
+            "normalTexture" : {
+                "index" : 8,
+                "texCoord" : 0
+            },
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0,
+                    0,
+                    0,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.029999999329447746
+            }
+        },
+        {
+            "emissiveFactor" : [
+                1,
+                1,
+                1
+            ],
+            "emissiveTexture" : {
+                "index" : 9,
+                "texCoord" : 0
+            },
+            "name" : "LabelMaterial",
+            "pbrMetallicRoughness" : {
+                "baseColorFactor" : [
+                    0,
+                    0,
+                    0,
+                    1
+                ],
+                "metallicFactor" : 0,
+                "roughnessFactor" : 0.8999999761581421
+            }
+        }
+    ],
+    "meshes" : [
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 0
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 1
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 2
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 3
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 4
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 5
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 6
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 7
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 8
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 9
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 10
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 11
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 12
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 13
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 14
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 15
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 16
+                }
+            ]
+        },
+        {
+            "name" : "ClearCoatSampleMesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 0,
+                        "NORMAL" : 1,
+                        "TEXCOORD_0" : 2
+                    },
+                    "indices" : 3,
+                    "material" : 17
+                }
+            ]
+        },
+        {
+            "name" : "Labels_Mesh",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 4,
+                        "NORMAL" : 5,
+                        "TEXCOORD_0" : 6
+                    },
+                    "indices" : 7,
+                    "material" : 18
+                }
+            ]
+        },
+        {
+            "name" : "Labels_Mesh.001",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 8,
+                        "NORMAL" : 9,
+                        "TEXCOORD_0" : 10
+                    },
+                    "indices" : 7,
+                    "material" : 18
+                }
+            ]
+        },
+        {
+            "name" : "Labels_Mesh.002",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 11,
+                        "NORMAL" : 12,
+                        "TEXCOORD_0" : 13
+                    },
+                    "indices" : 14,
+                    "material" : 18
+                }
+            ]
+        },
+        {
+            "name" : "Labels_Mesh.003",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 15,
+                        "NORMAL" : 16,
+                        "TEXCOORD_0" : 17
+                    },
+                    "indices" : 14,
+                    "material" : 18
+                }
+            ]
+        },
+        {
+            "name" : "Labels_Mesh.004",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 18,
+                        "NORMAL" : 19,
+                        "TEXCOORD_0" : 20
+                    },
+                    "indices" : 14,
+                    "material" : 18
+                }
+            ]
+        },
+        {
+            "name" : "Labels_Mesh.005",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 21,
+                        "NORMAL" : 22,
+                        "TEXCOORD_0" : 23
+                    },
+                    "indices" : 14,
+                    "material" : 18
+                }
+            ]
+        },
+        {
+            "name" : "Labels_Mesh.006",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 24,
+                        "NORMAL" : 25,
+                        "TEXCOORD_0" : 26
+                    },
+                    "indices" : 14,
+                    "material" : 18
+                }
+            ]
+        },
+        {
+            "name" : "Labels_Mesh.007",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 27,
+                        "NORMAL" : 28,
+                        "TEXCOORD_0" : 29
+                    },
+                    "indices" : 14,
+                    "material" : 18
+                }
+            ]
+        },
+        {
+            "name" : "Labels_Mesh.008",
+            "primitives" : [
+                {
+                    "attributes" : {
+                        "POSITION" : 30,
+                        "NORMAL" : 31,
+                        "TEXCOORD_0" : 32
+                    },
+                    "indices" : 7,
+                    "material" : 18
+                }
+            ]
+        }
+    ],
+    "textures" : [
+        {
+            "source" : 0
+        },
+        {
+            "source" : 1
+        },
+        {
+            "source" : 2
+        },
+        {
+            "source" : 3
+        },
+        {
+            "source" : 3
+        },
+        {
+            "source" : 4
+        },
+        {
+            "source" : 3
+        },
+        {
+            "source" : 3
+        },
+        {
+            "source" : 3
+        },
+        {
+            "source" : 5
+        }
+    ],
+    "images" : [
+        {
+            "mimeType" : "image/png",
+            "name" : "PartialCoating",
+            "uri" : "PartialCoating.png"
+        },
+        {
+            "mimeType" : "image/png",
+            "name" : "PartialCoating_Alpha",
+            "uri" : "PartialCoating_Alpha.png"
+        },
+        {
+            "mimeType" : "image/png",
+            "name" : "RoughnessStripes",
+            "uri" : "RoughnessStripes.png"
+        },
+        {
+            "mimeType" : "image/png",
+            "name" : "RibsNormal",
+            "uri" : "RibsNormal.png"
+        },
+        {
+            "mimeType" : "image/jpeg",
+            "name" : "PlasticWrap_normals",
+            "uri" : "PlasticWrap_normals.jpg"
+        },
+        {
+            "mimeType" : "image/png",
+            "name" : "ClearCoatLabels",
+            "uri" : "ClearCoatLabels.png"
+        }
+    ],
+    "accessors" : [
+        {
+            "bufferView" : 0,
+            "componentType" : 5126,
+            "count" : 1113,
+            "max" : [
+                1,
+                1,
+                1.0499999523162842
+            ],
+            "min" : [
+                -1,
+                -1,
+                -0.06000000983476639
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 1,
+            "componentType" : 5126,
+            "count" : 1113,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 2,
+            "componentType" : 5126,
+            "count" : 1113,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 3,
+            "componentType" : 5123,
+            "count" : 6180,
+            "type" : "SCALAR"
+        },
+        {
+            "bufferView" : 4,
+            "componentType" : 5126,
+            "count" : 8,
+            "max" : [
+                1.0280373096466064,
+                0.23501670360565186,
+                3.8289083903464416e-08
+            ],
+            "min" : [
+                -0.968224287033081,
+                -0.2350165843963623,
+                -0.010000125505030155
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 5,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 6,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 7,
+            "componentType" : 5123,
+            "count" : 12,
+            "type" : "SCALAR"
+        },
+        {
+            "bufferView" : 8,
+            "componentType" : 5126,
+            "count" : 8,
+            "max" : [
+                2,
+                0.23026323318481445,
+                3.751463495405005e-08
+            ],
+            "min" : [
+                -2,
+                -0.23026317358016968,
+                -0.010000579059123993
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 9,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 10,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 11,
+            "componentType" : 5126,
+            "count" : 8,
+            "max" : [
+                2,
+                0.2302631139755249,
+                3.7514624295909016e-08
+            ],
+            "min" : [
+                -2,
+                -0.23026323318481445,
+                -0.010000428184866905
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 12,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 13,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 14,
+            "componentType" : 5123,
+            "count" : 12,
+            "type" : "SCALAR"
+        },
+        {
+            "bufferView" : 15,
+            "componentType" : 5126,
+            "count" : 8,
+            "max" : [
+                2,
+                0.22039484977722168,
+                3.590687924770464e-08
+            ],
+            "min" : [
+                -2,
+                -0.22039473056793213,
+                -0.010000280104577541
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 16,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 17,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 18,
+            "componentType" : 5126,
+            "count" : 8,
+            "max" : [
+                2,
+                0.21764808893203735,
+                3.545937588000925e-08
+            ],
+            "min" : [
+                -2,
+                -0.21764802932739258,
+                -0.010000137612223625
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 19,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 20,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 21,
+            "componentType" : 5126,
+            "count" : 8,
+            "max" : [
+                2,
+                0.20775499939918518,
+                3.3847587843638394e-08
+            ],
+            "min" : [
+                -2,
+                -0.20775499939918518,
+                -0.009999996051192284
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 22,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 23,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 24,
+            "componentType" : 5126,
+            "count" : 8,
+            "max" : [
+                2,
+                0.22341907024383545,
+                3.6399587344249085e-08
+            ],
+            "min" : [
+                -2,
+                -0.22341907024383545,
+                -0.009999859146773815
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 25,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 26,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 27,
+            "componentType" : 5126,
+            "count" : 8,
+            "max" : [
+                0.9169960618019104,
+                0.22670458257198334,
+                3.69348676088066e-08
+            ],
+            "min" : [
+                -0.9199233651161194,
+                -0.22670456767082214,
+                -0.010000176727771759
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 28,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 29,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC2"
+        },
+        {
+            "bufferView" : 30,
+            "componentType" : 5126,
+            "count" : 8,
+            "max" : [
+                0.8968609571456909,
+                0.20853587985038757,
+                3.397480696776256e-08
+            ],
+            "min" : [
+                -0.9147982001304626,
+                -0.2085357904434204,
+                -0.010000113397836685
+            ],
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 31,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC3"
+        },
+        {
+            "bufferView" : 32,
+            "componentType" : 5126,
+            "count" : 8,
+            "type" : "VEC2"
+        }
+    ],
+    "bufferViews" : [
+        {
+            "buffer" : 0,
+            "byteLength" : 13356,
+            "byteOffset" : 0
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 13356,
+            "byteOffset" : 13356
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 8904,
+            "byteOffset" : 26712
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 12360,
+            "byteOffset" : 35616
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 47976
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 48072
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 64,
+            "byteOffset" : 48168
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 24,
+            "byteOffset" : 48232
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 48256
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 48352
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 64,
+            "byteOffset" : 48448
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 48512
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 48608
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 64,
+            "byteOffset" : 48704
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 24,
+            "byteOffset" : 48768
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 48792
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 48888
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 64,
+            "byteOffset" : 48984
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 49048
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 49144
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 64,
+            "byteOffset" : 49240
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 49304
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 49400
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 64,
+            "byteOffset" : 49496
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 49560
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 49656
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 64,
+            "byteOffset" : 49752
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 49816
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 49912
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 64,
+            "byteOffset" : 50008
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 50072
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 96,
+            "byteOffset" : 50168
+        },
+        {
+            "buffer" : 0,
+            "byteLength" : 64,
+            "byteOffset" : 50264
+        }
+    ],
+    "buffers" : [
+        {
+            "byteLength" : 50328,
+            "uri" : "ClearCoatTest.bin"
+        }
+    ]
+}

BIN
test/models/glTF2/ClearCoat-glTF/PartialCoating.png


BIN
test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png


BIN
test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg


BIN
test/models/glTF2/ClearCoat-glTF/RibsNormal.png


BIN
test/models/glTF2/ClearCoat-glTF/RoughnessStripes.png


+ 2 - 2
test/unit/utTypes.cpp

@@ -53,8 +53,8 @@ class utTypes : public ::testing::Test {
 TEST_F( utTypes, Color3dCpmpareOpTest ) {
 TEST_F( utTypes, Color3dCpmpareOpTest ) {
     aiColor3D col1( 1, 2, 3 );
     aiColor3D col1( 1, 2, 3 );
     aiColor3D col2( 4, 5, 6 );
     aiColor3D col2( 4, 5, 6 );
-    aiColor3D col3( col1 );
-    
+    const aiColor3D &col3(col1);
+
     EXPECT_FALSE( col1 == col2 );
     EXPECT_FALSE( col1 == col2 );
     EXPECT_FALSE( col2 == col3 );
     EXPECT_FALSE( col2 == col3 );
     EXPECT_TRUE( col1 == col3 );
     EXPECT_TRUE( col1 == col3 );

+ 157 - 20
test/unit/utglTF2ImportExport.cpp

@@ -57,10 +57,9 @@ using namespace Assimp;
 
 
 class utglTF2ImportExport : public AbstractImportExportBase {
 class utglTF2ImportExport : public AbstractImportExportBase {
 public:
 public:
-    virtual bool importerTest() {
+    virtual bool importerMatTest(const char *file, bool spec_gloss, std::array<aiTextureMapMode, 2> exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }) {
         Assimp::Importer importer;
         Assimp::Importer importer;
-        const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf",
-                aiProcess_ValidateDataStructure);
+        const aiScene *scene = importer.ReadFile(file, aiProcess_ValidateDataStructure);
         EXPECT_NE(scene, nullptr);
         EXPECT_NE(scene, nullptr);
         if (!scene) {
         if (!scene) {
             return false;
             return false;
@@ -72,13 +71,49 @@ public:
         }
         }
         const aiMaterial *material = scene->mMaterials[0];
         const aiMaterial *material = scene->mMaterials[0];
 
 
+        // This Material should be a PBR
+        aiShadingMode shadingMode;
+        EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_SHADING_MODEL, shadingMode));
+        EXPECT_EQ(aiShadingMode_PBR_BRDF, shadingMode);
+
+        // Should import the texture as diffuse and as base color
         aiString path;
         aiString path;
-        aiTextureMapMode modes[2];
+        std::array<aiTextureMapMode,2> modes;
         EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr,
         EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr,
-                                            nullptr, nullptr, modes));
+                                            nullptr, nullptr, modes.data()));
+        EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png");
+        EXPECT_EQ(exp_modes, modes);
+
+        // Also as Base Color
+        EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_BASE_COLOR, 0, &path, nullptr, nullptr,
+                                            nullptr, nullptr, modes.data()));
         EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png");
         EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png");
-        EXPECT_EQ(modes[0], aiTextureMapMode_Mirror);
-        EXPECT_EQ(modes[1], aiTextureMapMode_Clamp);
+        EXPECT_EQ(exp_modes, modes);
+
+        // Should have a MetallicFactor (default is 1.0)
+        ai_real metal_factor = ai_real(0.5);
+        EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_METALLIC_FACTOR, metal_factor));
+        EXPECT_EQ(ai_real(0.0), metal_factor);
+
+        // And a roughness factor (default is 1.0)
+        ai_real roughness_factor = ai_real(0.5);
+        EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughness_factor));
+        EXPECT_EQ(ai_real(1.0), roughness_factor);
+
+        aiColor3D spec_color = { 0, 0, 0 };
+        ai_real glossiness = ai_real(0.5);
+        if (spec_gloss) {
+            EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color));
+            constexpr ai_real spec_val(0.20000000298023225); // From the file
+            EXPECT_EQ(spec_val, spec_color.r);
+            EXPECT_EQ(spec_val, spec_color.g);
+            EXPECT_EQ(spec_val, spec_color.b);
+            EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness));
+            EXPECT_EQ(ai_real(1.0), glossiness);
+        } else {
+            EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color));
+            EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness));
+        }
 
 
         return true;
         return true;
     }
     }
@@ -105,14 +140,89 @@ public:
 };
 };
 
 
 TEST_F(utglTF2ImportExport, importglTF2FromFileTest) {
 TEST_F(utglTF2ImportExport, importglTF2FromFileTest) {
-    EXPECT_TRUE(importerTest());
+    EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", false, {aiTextureMapMode_Mirror, aiTextureMapMode_Clamp}));
 }
 }
 
 
 TEST_F(utglTF2ImportExport, importBinaryglTF2FromFileTest) {
 TEST_F(utglTF2ImportExport, importBinaryglTF2FromFileTest) {
     EXPECT_TRUE(binaryImporterTest());
     EXPECT_TRUE(binaryImporterTest());
 }
 }
 
 
+TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) {
+    EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true));
+}
+
+void VerifyClearCoatScene(const aiScene *scene) {
+        ASSERT_NE(nullptr, scene);
+
+    ASSERT_TRUE(scene->HasMaterials());
+
+    // Find a specific Clearcoat material and check the values
+    const aiString partial_coated("Partial_Coated");
+    bool found_partial_coat = false;
+    for (size_t i = 0; i < scene->mNumMaterials; ++i) {
+        const aiMaterial *material = scene->mMaterials[i];
+        ASSERT_NE(nullptr, material);
+        if (material->GetName() == partial_coated) {
+            found_partial_coat = true;
+
+            ai_real clearcoat_factor(0.0f);
+            EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat_factor));
+            EXPECT_EQ(ai_real(1.0f), clearcoat_factor);
+
+            ai_real clearcoat_rough_factor(0.0f);
+            EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat_rough_factor));
+            EXPECT_EQ(ai_real(0.03f), clearcoat_rough_factor);
+
+            // Should import the texture as diffuse and as base color
+            aiString path;
+            std::array<aiTextureMapMode, 2> modes;
+            static const std::array<aiTextureMapMode, 2> exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap };
+            EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_CLEARCOAT_TEXTURE, &path, nullptr, nullptr,
+                                                nullptr, nullptr, modes.data()));
+            EXPECT_STREQ(path.C_Str(), "PartialCoating.png");
+            EXPECT_EQ(exp_modes, modes);
+        }
+    }
+    EXPECT_TRUE(found_partial_coat);
+}
+
+TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_clearcoat) {
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure);
+    VerifyClearCoatScene(scene);
+}
+
 #ifndef ASSIMP_BUILD_NO_EXPORT
 #ifndef ASSIMP_BUILD_NO_EXPORT
+
+TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_clearcoat) {
+    {
+        Assimp::Importer importer;
+        Assimp::Exporter exporter;
+        const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure);
+        ASSERT_NE(nullptr, scene);
+        // Export
+        EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb"));
+    }
+
+    // And re-import
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb", aiProcess_ValidateDataStructure);
+    VerifyClearCoatScene(scene);
+}
+
+TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossiness) {
+    Assimp::Importer importer;
+    Assimp::Exporter exporter;
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf",
+            aiProcess_ValidateDataStructure);
+    EXPECT_NE(nullptr, scene);
+    // Export
+    EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb"));
+
+    // And re-import
+    EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", true));
+}
+
 TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) {
 TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) {
     Assimp::Importer importer;
     Assimp::Importer importer;
     Assimp::Exporter exporter;
     Assimp::Exporter exporter;
@@ -130,6 +240,7 @@ TEST_F(utglTF2ImportExport, importglTF2EmbeddedAndExportToOBJ) {
     EXPECT_NE(nullptr, scene);
     EXPECT_NE(nullptr, scene);
     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "obj", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured_out.obj"));
     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "obj", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured_out.obj"));
 }
 }
+
 #endif // ASSIMP_BUILD_NO_EXPORT
 #endif // ASSIMP_BUILD_NO_EXPORT
 
 
 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModePointsWithoutIndices) {
 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModePointsWithoutIndices) {
@@ -492,32 +603,58 @@ TEST_F(utglTF2ImportExport, sceneMetadata) {
 }
 }
 
 
 TEST_F(utglTF2ImportExport, texcoords) {
 TEST_F(utglTF2ImportExport, texcoords) {
+
     Assimp::Importer importer;
     Assimp::Importer importer;
-    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf",
-            aiProcess_ValidateDataStructure);
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure);
     ASSERT_NE(scene, nullptr);
     ASSERT_NE(scene, nullptr);
-
     ASSERT_TRUE(scene->HasMaterials());
     ASSERT_TRUE(scene->HasMaterials());
     const aiMaterial *material = scene->mMaterials[0];
     const aiMaterial *material = scene->mMaterials[0];
 
 
     aiString path;
     aiString path;
+    unsigned int uvIndex = 255;
     aiTextureMapMode modes[2];
     aiTextureMapMode modes[2];
-    EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr,
-                                        nullptr, nullptr, modes));
+    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_STREQ(path.C_Str(), "texture.png");
+    EXPECT_EQ(uvIndex, 0);
+
+    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);
+}
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+
+TEST_F(utglTF2ImportExport, texcoords_export) {
+    {
+        Assimp::Importer importer;
+        Assimp::Exporter exporter;
+        const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure);
+        ASSERT_NE(scene, nullptr);
+        ASSERT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf_out.glb"));
+    }
 
 
-    int uvIndex = -1;
-    EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_DIFFUSE, 0), &uvIndex), aiReturn_SUCCESS);
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure);
+    ASSERT_NE(scene, nullptr);
+
+    ASSERT_TRUE(scene->HasMaterials());
+    const aiMaterial *material = scene->mMaterials[0];
+
+    aiString path;
+    unsigned int uvIndex = 255;
+    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, 0);
 
 
-    // Using manual macro expansion of AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE here.
-    // The following works with some but not all compilers:
-    // #define APPLY(X, Y) X(Y)
-    // ..., APPLY(AI_MATKEY_GLTF_TEXTURE_TEXCOORD, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE), ...
-    EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_UNKNOWN, 0), &uvIndex), aiReturn_SUCCESS);
+    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, 1);
 }
 }
 
 
+#endif // ASSIMP_BUILD_NO_EXPORT
 TEST_F(utglTF2ImportExport, recursive_nodes) {
 TEST_F(utglTF2ImportExport, recursive_nodes) {
     Assimp::Importer importer;
     Assimp::Importer importer;
     const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/RecursiveNodes/RecursiveNodes.gltf", aiProcess_ValidateDataStructure);
     const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/RecursiveNodes/RecursiveNodes.gltf", aiProcess_ValidateDataStructure);

+ 3 - 2
tools/assimp_view/AnimEvaluator.cpp

@@ -39,9 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 */
 */
 
 
-#include "assimp_view.h"
+#include "AnimEvaluator.h"
 
 
-#include <tuple>
+#include <assimp/anim.h>
+#include <assimp/ai_assert.h>
 
 
 using namespace AssimpView;
 using namespace AssimpView;
 
 

+ 15 - 9
tools/assimp_view/AnimEvaluator.h

@@ -1,4 +1,3 @@
-/** Calculates a pose for a given time of an animation */
 /*
 /*
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
@@ -40,11 +39,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 ---------------------------------------------------------------------------
 */
 */
 
 
+#pragma once
 #ifndef AV_ANIMEVALUATOR_H_INCLUDED
 #ifndef AV_ANIMEVALUATOR_H_INCLUDED
 #define AV_ANIMEVALUATOR_H_INCLUDED
 #define AV_ANIMEVALUATOR_H_INCLUDED
 
 
+/** Calculates a pose for a given time of an animation */
+
 #include <tuple>
 #include <tuple>
 #include <vector>
 #include <vector>
+#include <assimp/matrix4x4.h>
+
+struct aiAnimation;
 
 
 namespace AssimpView {
 namespace AssimpView {
 
 
@@ -63,18 +68,19 @@ public:
     /// @brief  The class destructor.
     /// @brief  The class destructor.
     ~AnimEvaluator();
     ~AnimEvaluator();
 
 
-    /** Evaluates the animation tracks for a given time stamp. The calculated pose can be retrieved as a
-     * array of transformation matrices afterwards by calling GetTransformations().
-     * @param pTime The time for which you want to evaluate the animation, in seconds. Will be mapped into the animation cycle, so
-     *   it can be an arbitrary value. Best use with ever-increasing time stamps.
-     */
+    /// @brief Evaluates the animation tracks for a given time stamp. 
+    /// The calculated pose can be retrieved as an array of transformation 
+    /// matrices afterwards by calling GetTransformations().
+    /// @param pTime    The time for which you want to evaluate the animation, in seconds. 
+    ///                 Will be mapped into the animation cycle, so it can get an arbitrary 
+    ///                 value. Best use with ever-increasing time stamps.
     void Evaluate(double pTime);
     void Evaluate(double pTime);
 
 
-    /** Returns the transform matrices calculated at the last Evaluate() call. The array matches the mChannels array of
-     * the aiAnimation. */
+    /// @brief  Returns the transform matrices calculated at the last Evaluate() call. 
+    ///         The array matches the mChannels array of the aiAnimation.
     const std::vector<aiMatrix4x4> &GetTransformations() const { return mTransforms; }
     const std::vector<aiMatrix4x4> &GetTransformations() const { return mTransforms; }
 
 
-protected:
+private:
     const aiAnimation *mAnim;
     const aiAnimation *mAnim;
     double mLastTime;
     double mLastTime;
     std::vector<std::tuple<unsigned int, unsigned int, unsigned int>> mLastPositions;
     std::vector<std::tuple<unsigned int, unsigned int, unsigned int>> mLastPositions;

+ 3 - 4
tools/assimp_view/assimp_view.cpp

@@ -489,7 +489,7 @@ int CreateAssetData() {
             nidx = 3;
             nidx = 3;
             break;
             break;
         default:
         default:
-            ai_assert(false);
+            CLogWindow::Instance().WriteLine("Unknown primitiv type");
             break;
             break;
         };
         };
 
 
@@ -500,8 +500,7 @@ int CreateAssetData() {
         // check whether we can use 16 bit indices
         // check whether we can use 16 bit indices
         if (numIndices >= 65536) {
         if (numIndices >= 65536) {
             // create 32 bit index buffer
             // create 32 bit index buffer
-            if (FAILED(g_piDevice->CreateIndexBuffer(4 *
-                                                             numIndices,
+            if (FAILED(g_piDevice->CreateIndexBuffer(4 * numIndices,
                         D3DUSAGE_WRITEONLY | dwUsage,
                         D3DUSAGE_WRITEONLY | dwUsage,
                         D3DFMT_INDEX32,
                         D3DFMT_INDEX32,
                         D3DPOOL_DEFAULT,
                         D3DPOOL_DEFAULT,
@@ -523,7 +522,7 @@ int CreateAssetData() {
         } else {
         } else {
             // create 16 bit index buffer
             // create 16 bit index buffer
             if (FAILED(g_piDevice->CreateIndexBuffer(2 *
             if (FAILED(g_piDevice->CreateIndexBuffer(2 *
-                                                             numIndices,
+numIndices,
                         D3DUSAGE_WRITEONLY | dwUsage,
                         D3DUSAGE_WRITEONLY | dwUsage,
                         D3DFMT_INDEX16,
                         D3DFMT_INDEX16,
                         D3DPOOL_DEFAULT,
                         D3DPOOL_DEFAULT,