Jelajahi Sumber

Merge branch 'master' into zero

Kim Kulling 4 tahun lalu
induk
melakukan
012ce30577

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

@@ -64,10 +64,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 struct aiScene;
 struct aiScene;
 struct aiNode;
 struct aiNode;
 struct aiLight;
 struct aiLight;
-//struct aiMaterial;
 
 
-namespace Assimp
-{
+namespace Assimp {
     class IOSystem;
     class IOSystem;
     class IOStream;
     class IOStream;
     class ExportProperties;
     class ExportProperties;

+ 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;

+ 51 - 46
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,8 @@ 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;
+
     //! 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 +426,8 @@ 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);
 };
 };
 
 
 //
 //
@@ -834,50 +885,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 +903,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);
 };
 };

+ 44 - 38
code/AssetLib/glTF2/glTF2Asset.inl

@@ -304,6 +304,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 +367,12 @@ 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);
+    }
+}
+
 #ifdef ASSIMP_ENABLE_DRACO
 #ifdef ASSIMP_ENABLE_DRACO
 
 
 template <typename T>
 template <typename T>
@@ -569,6 +612,7 @@ 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);
 
 
     Ref<T> result = Add(inst.release());
     Ref<T> result = Add(inst.release());
     mRecursiveReferenceCheck.erase(i);
     mRecursiveReferenceCheck.erase(i);
@@ -1685,42 +1729,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;
@@ -1777,8 +1785,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");

+ 161 - 121
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;
+}
+
+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;
     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,17 +775,17 @@ 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);
 
 
         aiString alphaMode;
         aiString alphaMode;
 
 
-        if (mat->Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) {
+        if (mat.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) {
             m->alphaMode = alphaMode.C_Str();
             m->alphaMode = alphaMode.C_Str();
         } else {
         } else {
             float opacity;
             float opacity;
 
 
-            if (mat->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) {
+            if (mat.Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) {
                 if (opacity < 1) {
                 if (opacity < 1) {
                     m->alphaMode = "BLEND";
                     m->alphaMode = "BLEND";
                     m->pbrMetallicRoughness.baseColorFactor[3] *= opacity;
                     m->pbrMetallicRoughness.baseColorFactor[3] *= opacity;
@@ -712,85 +793,44 @@ void glTF2Exporter::ExportMaterials()
             }
             }
         }
         }
 
 
-        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);
+                }
+            }
         }
         }
     }
     }
 }
 }

+ 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();

+ 49 - 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;
@@ -268,52 +276,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 +503,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]);
@@ -1002,9 +1016,9 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
             }
             }
         }
         }
 
 
-        if (node.extensions) {
+        if (node.customExtensions) {
             ainode->mMetaData = new aiMetadata;
             ainode->mMetaData = new aiMetadata;
-            ParseExtensions(ainode->mMetaData, node.extensions);
+            ParseExtensions(ainode->mMetaData, node.customExtensions);
         }
         }
 
 
         GetNodeTransform(ainode->mTransformation, node);
         GetNodeTransform(ainode->mTransformation, node);
@@ -1498,7 +1512,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 +1524,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);
+        }
     }
     }
 }
 }
 
 

+ 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:

+ 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
 
 

TEMPAT SAMPAH
test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png


TEMPAT SAMPAH
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"
+        }
+    ]
+}

TEMPAT SAMPAH
test/models/glTF2/ClearCoat-glTF/PartialCoating.png


TEMPAT SAMPAH
test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png


TEMPAT SAMPAH
test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg


TEMPAT SAMPAH
test/models/glTF2/ClearCoat-glTF/RibsNormal.png


TEMPAT SAMPAH
test/models/glTF2/ClearCoat-glTF/RoughnessStripes.png


+ 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);