Browse Source

Merge branch 'master' into issue_3678

Kim Kulling 4 years ago
parent
commit
cdb6a62cdb

+ 0 - 1
.github/FUNDING.yml

@@ -1,2 +1 @@
-patreon: assimp
 open_collective: assimp

+ 1 - 1
CMakeLists.txt

@@ -454,7 +454,7 @@ IF(ASSIMP_HUNTER_ENABLED)
 ELSE()
   # If the zlib is already found outside, add an export in case assimpTargets can't find it.
   IF( ZLIB_FOUND )
-    INSTALL( TARGETS zlib
+    INSTALL( TARGETS zlib zlibstatic
         EXPORT "${TARGETS_EXPORT_NAME}")
   ENDIF()
 

+ 25 - 0
Dockerfile

@@ -0,0 +1,25 @@
+FROM ubuntu:14.04
+
+RUN apt-get update && apt-get install -y \
+    git cmake build-essential software-properties-common
+
+RUN add-apt-repository ppa:ubuntu-toolchain-r/test && apt-get update && apt-get install -y gcc-4.9 g++-4.9 && \
+    cd /usr/bin && \
+    rm gcc g++ cpp && \
+    ln -s gcc-4.9 gcc && \
+    ln -s g++-4.9 g++ && \
+    ln -s cpp-4.9 cpp
+
+WORKDIR /opt
+
+# Build Assimp
+RUN git clone https://github.com/assimp/assimp.git /opt/assimp
+
+WORKDIR /opt/assimp
+
+RUN git checkout master \
+    && mkdir build && cd build && \
+    cmake \
+    -DCMAKE_BUILD_TYPE=Release \
+    .. && \
+    make && make install

+ 0 - 6
Readme.md

@@ -76,9 +76,6 @@ For more information, visit [our website](http://assimp.org/). Or check out the
 
 If the docs don't solve your problem, ask on [StackOverflow with the assimp-tag](http://stackoverflow.com/questions/tagged/assimp?sort=newest). If you think you found a bug, please open an issue on Github.
 
-For development discussions, there is also a (very low-volume) mailing list, _assimp-discussions_
-  [(subscribe here)]( https://lists.sourceforge.net/lists/listinfo/assimp-discussions)
-
 Open Asset Import Library is a library to load various 3d file formats into a shared, in-memory format. It supports more than __40 file formats__ for import and a growing selection of file formats for export.
 
 And we also have a Gitter-channel:Gitter [![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)<br>
@@ -103,9 +100,6 @@ Become a financial contributor and help us sustain our community. [[Contribute](
 
 <a href="https://opencollective.com/assimp"><img src="https://opencollective.com/assimp/individuals.svg?width=890"></a>
 
-Monthly donations via Patreon:
-<br>[![Patreon](https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png)](http://www.patreon.com/assimp)
-
 
 #### Organizations
 

+ 7 - 2
code/AssetLib/3DS/3DSExporter.cpp

@@ -444,7 +444,7 @@ void Discreet3DSExporter::WriteMeshes() {
             const uint16_t count = static_cast<uint16_t>(mesh.mNumVertices);
             writer.PutU2(count);
             for (unsigned int i = 0; i < mesh.mNumVertices; ++i) {
-                const aiVector3D &v = trafo * mesh.mVertices[i];
+                const aiVector3D &v = mesh.mVertices[i];
                 writer.PutF4(v.x);
                 writer.PutF4(v.y);
                 writer.PutF4(v.z);
@@ -506,11 +506,16 @@ void Discreet3DSExporter::WriteMeshes() {
         // Transformation matrix by which the mesh vertices have been pre-transformed with.
         {
             ChunkWriter curChunk(writer, Discreet3DS::CHUNK_TRMATRIX);
-            for (unsigned int r = 0; r < 4; ++r) {
+            // Store rotation 3x3 matrix row wise
+            for (unsigned int r = 0; r < 3; ++r) {
                 for (unsigned int c = 0; c < 3; ++c) {
                     writer.PutF4(trafo[r][c]);
                 }
             }
+            // Store translation sub vector column wise
+            for (unsigned int r = 0; r < 3; ++r) {
+                writer.PutF4(trafo[r][3]);
+            }
         }
     }
 }

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

@@ -981,9 +981,9 @@ void Discreet3DSImporter::ParseMeshChunk() {
         mMesh.mMat.a3 = stream->GetF4();
         mMesh.mMat.b3 = stream->GetF4();
         mMesh.mMat.c3 = stream->GetF4();
-        mMesh.mMat.a4 = stream->GetF4();
-        mMesh.mMat.b4 = stream->GetF4();
-        mMesh.mMat.c4 = stream->GetF4();
+        mMesh.mMat.d1 = stream->GetF4();
+        mMesh.mMat.d2 = stream->GetF4();
+        mMesh.mMat.d3 = stream->GetF4();
     } break;
 
     case Discreet3DS::CHUNK_MAPLIST: {

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

@@ -116,7 +116,7 @@ public:
             Mask = 0xf,
         };
 
-        inline const uint8_t GetType() const { return (flags & Mask); }
+        inline uint8_t GetType() const { return (flags & Mask); }
     };
 
     // Represents an AC3D object

+ 108 - 1
code/AssetLib/FBX/FBXExporter.cpp

@@ -812,6 +812,18 @@ void FBXExporter::WriteDefinitions ()
     // Geometry / FbxMesh
     // <~~ aiMesh
     count = mScene->mNumMeshes;
+
+    // Blendshapes are considered Geometry
+    int32_t bsDeformerCount=0;
+    for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
+        aiMesh* m = mScene->mMeshes[mi];
+        if (m->mNumAnimMeshes > 0) {
+          count+=m->mNumAnimMeshes;
+          bsDeformerCount+=m->mNumAnimMeshes; // One deformer per blendshape
+          bsDeformerCount++;                  // Plus one master blendshape deformer
+        }
+    }
+
     if (count) {
         n = FBX::Node("ObjectType", "Geometry");
         n.AddChild("Count", count);
@@ -978,7 +990,7 @@ void FBXExporter::WriteDefinitions ()
     }
 
     // Deformer
-    count = int32_t(count_deformers(mScene));
+    count = int32_t(count_deformers(mScene))+bsDeformerCount;
     if (count) {
         n = FBX::Node("ObjectType", "Deformer");
         n.AddChild("Count", count);
@@ -1363,6 +1375,7 @@ void FBXExporter::WriteObjects ()
         n.End(outstream, binary, indent, true);
     }
 
+
     // aiMaterial
     material_uids.clear();
     for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
@@ -1697,6 +1710,100 @@ void FBXExporter::WriteObjects ()
         }
     }
 
+    // Blendshapes, if any
+    for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
+      const aiMesh* m = mScene->mMeshes[mi];
+      if (m->mNumAnimMeshes == 0) {
+        continue;
+      }
+      // make a deformer for this mesh
+      int64_t deformer_uid = generate_uid();
+      FBX::Node dnode("Deformer");
+      dnode.AddProperties(deformer_uid, m->mName.data + FBX::SEPARATOR + "Blendshapes", "BlendShape");
+      dnode.AddChild("Version", int32_t(101));
+      dnode.Dump(outstream, binary, indent);
+      // connect it
+      connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]);
+      std::vector<int32_t> vertex_indices = vVertexIndice[mi];
+
+      for (unsigned int am = 0; am < m->mNumAnimMeshes; ++am) {
+        aiAnimMesh *pAnimMesh = m->mAnimMeshes[am];
+        std::string blendshape_name = pAnimMesh->mName.data;
+
+        // start the node record
+        FBX::Node bsnode("Geometry");
+        int64_t blendshape_uid = generate_uid();
+        mesh_uids.push_back(blendshape_uid);
+        bsnode.AddProperty(blendshape_uid);
+        bsnode.AddProperty(blendshape_name + FBX::SEPARATOR + "Blendshape");
+        bsnode.AddProperty("Shape");
+        bsnode.AddChild("Version", int32_t(100));        
+        bsnode.Begin(outstream, binary, indent);
+        bsnode.DumpProperties(outstream, binary, indent);
+        bsnode.EndProperties(outstream, binary, indent);
+        bsnode.BeginChildren(outstream, binary, indent);
+        indent++;
+        if (pAnimMesh->HasPositions()) {
+          std::vector<int32_t>shape_indices;
+          std::vector<double>pPositionDiff;
+          std::vector<double>pNormalDiff;
+
+          for (unsigned int vt = 0; vt < vertex_indices.size(); ++vt) {
+              aiVector3D pDiff = (pAnimMesh->mVertices[vertex_indices[vt]] - m->mVertices[vertex_indices[vt]]);
+              if(pDiff.Length()>1e-8){
+                shape_indices.push_back(vertex_indices[vt]);
+                pPositionDiff.push_back(pDiff[0]);
+                pPositionDiff.push_back(pDiff[1]);
+                pPositionDiff.push_back(pDiff[2]);
+
+                if (pAnimMesh->HasNormals()) {
+                    aiVector3D nDiff = (pAnimMesh->mNormals[vertex_indices[vt]] - m->mNormals[vertex_indices[vt]]);
+                    pNormalDiff.push_back(nDiff[0]);
+                    pNormalDiff.push_back(nDiff[1]);
+                    pNormalDiff.push_back(nDiff[2]);
+                }
+              }
+          }
+
+          FBX::Node::WritePropertyNode(
+              "Indexes", shape_indices, outstream, binary, indent
+          );
+
+          FBX::Node::WritePropertyNode(
+              "Vertices", pPositionDiff, outstream, binary, indent
+          );
+
+          if (pNormalDiff.size()>0) {
+            FBX::Node::WritePropertyNode(
+                "Normals", pNormalDiff, outstream, binary, indent
+            );
+          }
+        }
+        indent--;
+        bsnode.End(outstream, binary, indent, true);
+
+        // Add blendshape Channel Deformer
+        FBX::Node sdnode("Deformer");
+        const int64_t blendchannel_uid = generate_uid();
+        sdnode.AddProperties(
+            blendchannel_uid, blendshape_name + FBX::SEPARATOR + "SubDeformer", "BlendShapeChannel"
+        );
+        sdnode.AddChild("Version", int32_t(100));
+        sdnode.AddChild("DeformPercent", int32_t(100));
+        FBX::Node p("Properties70");
+        p.AddP70numberA("DeformPercent", 100.);
+        sdnode.AddChild(p);
+        // TODO: Normally just one weight per channel, adding stub for later development
+        std::vector<float>fFullWeights;
+        fFullWeights.push_back(0.);
+        sdnode.AddChild("FullWeights", fFullWeights);
+        sdnode.Dump(outstream, binary, indent);
+
+        connections.emplace_back("C", "OO", blendchannel_uid, deformer_uid);
+        connections.emplace_back("C", "OO", blendshape_uid, blendchannel_uid);
+      }
+    }
+
     // bones.
     //
     // output structure:

+ 6 - 2
code/AssetLib/Step/StepExporter.cpp

@@ -302,6 +302,10 @@ void StepExporter::WriteFile()
             dv23.Normalize();
             dv31.Normalize();
             dv13.Normalize();
+            
+            aiVector3D dvY = dv12;
+            aiVector3D dvX = dvY ^ dv13;
+            dvX.Normalize();
 
             int pid1 = uniqueVerts.find(v1)->second;
             int pid2 = uniqueVerts.find(v2)->second;
@@ -337,8 +341,8 @@ void StepExporter::WriteFile()
             mOutput << "#" << sid+9 << "=PLANE('',#" << sid+10 << ")" << endstr;
             mOutput << "#" << sid+10 << "=AXIS2_PLACEMENT_3D('',#" << pid1 << ", #" << sid+11 << ",#" << sid+12 << ")" << endstr;
 
-            mOutput << "#" << sid+11 << "=DIRECTION('',(" << dv12.x << "," << dv12.y << "," << dv12.z << "))" << endstr;
-            mOutput << "#" << sid+12 << "=DIRECTION('',(" << dv13.x << "," << dv13.y << "," << dv13.z << "))" << endstr;
+            mOutput << "#" << sid + 11 << "=DIRECTION('',(" << dvX.x << "," << dvX.y << "," << dvX.z << "))" << endstr;
+            mOutput << "#" << sid + 12 << "=DIRECTION('',(" << dvY.x << "," << dvY.y << "," << dvY.z << "))" << endstr;
 
             mOutput << "#" << sid+13 << "=FACE_BOUND('',#" << sid+14 << ",.T.)" << endstr;
             mOutput << "#" << sid+14 << "=EDGE_LOOP('',(#" << sid+15 << ",#" << sid+16 << ",#" << sid+17 << "))" << endstr;

+ 12 - 0
code/AssetLib/glTF2/glTF2Asset.h

@@ -370,6 +370,13 @@ struct Object {
 
     //! Maps special IDs to another ID, where needed. Subclasses may override it (statically)
     static const char *TranslateId(Asset & /*r*/, const char *id) { return id; }
+
+    inline Value *FindString(Value &val, const char *id);
+    inline Value *FindNumber(Value &val, const char *id);
+    inline Value *FindUInt(Value &val, const char *id);
+    inline Value *FindArray(Value &val, const char *id);
+    inline Value *FindObject(Value &val, const char *id);
+    inline Value *FindExtension(Value &val, const char *extensionId);
 };
 
 //
@@ -780,6 +787,11 @@ struct Material : public Object {
     Material() { SetDefaults(); }
     void Read(Value &obj, Asset &r);
     void SetDefaults();
+
+    inline void SetTextureProperties(Asset &r, Value *prop, TextureInfo &out);
+    inline void ReadTextureProperty(Asset &r, Value &vals, const char *propName, TextureInfo &out);
+    inline void ReadTextureProperty(Asset &r, Value &vals, const char *propName, NormalTextureInfo &out);
+    inline void ReadTextureProperty(Asset &r, Value &vals, const char *propName, OcclusionTextureInfo &out);
 };
 
 //! A set of primitives to be rendered. A node can contain one or more meshes. A node's transform places the mesh in the scene.

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

@@ -180,41 +180,133 @@ inline Value *FindMember(Value &val, const char *id) {
     return (it != val.MemberEnd()) ? &it->value : nullptr;
 }
 
-inline Value *FindString(Value &val, const char *id) {
-    Value::MemberIterator it = val.FindMember(id);
-    return (it != val.MemberEnd() && it->value.IsString()) ? &it->value : nullptr;
+template<int N>
+inline void throwUnexpectedTypeError(const char (&expectedTypeName)[N], const char* memberId, const char* context, const char* extraContext) {
+    std::string fullContext = context;
+    if (extraContext && (strlen(extraContext) > 0))
+    {
+        fullContext = fullContext + " (" + extraContext + ")";
+    }
+    throw DeadlyImportError("Member \"", memberId, "\" was not of type \"", expectedTypeName, "\" when reading ", fullContext);
 }
 
-inline Value *FindNumber(Value &val, const char *id) {
-    Value::MemberIterator it = val.FindMember(id);
-    return (it != val.MemberEnd() && it->value.IsNumber()) ? &it->value : nullptr;
+// Look-up functions with type checks. Context and extra context help the user identify the problem if there's an error.
+
+inline Value *FindStringInContext(Value &val, const char *memberId, const char* context, const char* extraContext = nullptr) {
+    Value::MemberIterator it = val.FindMember(memberId);
+    if (it == val.MemberEnd()) {
+        return nullptr;
+    }
+    if (!it->value.IsString()) {
+        throwUnexpectedTypeError("string", memberId, context, extraContext);
+    }
+    return &it->value;
 }
 
-inline Value *FindUInt(Value &val, const char *id) {
-    Value::MemberIterator it = val.FindMember(id);
-    return (it != val.MemberEnd() && it->value.IsUint()) ? &it->value : nullptr;
+inline Value *FindNumberInContext(Value &val, const char *memberId, const char* context, const char* extraContext = nullptr) {
+    Value::MemberIterator it = val.FindMember(memberId);
+    if (it == val.MemberEnd()) {
+        return nullptr;
+    }
+    if (!it->value.IsNumber()) {
+        throwUnexpectedTypeError("number", memberId, context, extraContext);
+    }
+    return &it->value;
 }
 
-inline Value *FindArray(Value &val, const char *id) {
-    Value::MemberIterator it = val.FindMember(id);
-    return (it != val.MemberEnd() && it->value.IsArray()) ? &it->value : nullptr;
+inline Value *FindUIntInContext(Value &val, const char *memberId, const char* context, const char* extraContext = nullptr) {
+    Value::MemberIterator it = val.FindMember(memberId);
+    if (it == val.MemberEnd()) {
+        return nullptr;
+    }
+    if (!it->value.IsUint()) {
+        throwUnexpectedTypeError("uint", memberId, context, extraContext);
+    }
+    return &it->value;
 }
 
-inline Value *FindObject(Value &val, const char *id) {
-    Value::MemberIterator it = val.FindMember(id);
-    return (it != val.MemberEnd() && it->value.IsObject()) ? &it->value : nullptr;
+inline Value *FindArrayInContext(Value &val, const char *memberId, const char* context, const char* extraContext = nullptr) {
+    Value::MemberIterator it = val.FindMember(memberId);
+    if (it == val.MemberEnd()) {
+        return nullptr;
+    }
+    if (!it->value.IsArray()) {
+        throwUnexpectedTypeError("array", memberId, context, extraContext);
+    }
+    return &it->value;
 }
 
-inline Value *FindExtension(Value &val, const char *extensionId) {
-    if (Value *extensionList = FindObject(val, "extensions")) {
-        if (Value *extension = FindObject(*extensionList, extensionId)) {
+inline Value *FindObjectInContext(Value &val, const char *memberId, const char* context, const char* extraContext = nullptr) {
+    Value::MemberIterator it = val.FindMember(memberId);
+    if (it == val.MemberEnd()) {
+        return nullptr;
+    }
+    if (!it->value.IsObject()) {
+        throwUnexpectedTypeError("object", memberId, context, extraContext);
+    }
+    return &it->value;
+}
+
+inline Value *FindExtensionInContext(Value &val, const char *extensionId, const char* context, const char* extraContext = nullptr) {
+    if (Value *extensionList = FindObjectInContext(val, "extensions", context, extraContext)) {
+        if (Value *extension = FindObjectInContext(*extensionList, extensionId, context, extraContext)) {
             return extension;
         }
     }
     return nullptr;
 }
+
+// Overloads when the value is the document.
+
+inline Value *FindString(Document &doc, const char *memberId) {
+    return FindStringInContext(doc, memberId, "the document");
+}
+
+inline Value *FindNumber(Document &doc, const char *memberId) {
+    return FindNumberInContext(doc, memberId, "the document");
+}
+
+inline Value *FindUInt(Document &doc, const char *memberId) {
+    return FindUIntInContext(doc, memberId, "the document");
+}
+
+inline Value *FindArray(Document &val, const char *memberId) {
+    return FindArrayInContext(val, memberId, "the document");
+}
+
+inline Value *FindObject(Document &doc, const char *memberId) {
+    return FindObjectInContext(doc, memberId, "the document");
+}
+
+inline Value *FindExtension(Value &val, const char *extensionId) {
+    return FindExtensionInContext(val, extensionId, "the document");
+}
 } // namespace
 
+inline Value *Object::FindString(Value &val, const char *memberId) {
+    return FindStringInContext(val, memberId, id.c_str(), name.c_str());
+}
+
+inline Value *Object::FindNumber(Value &val, const char *memberId) {
+    return FindNumberInContext(val, memberId, id.c_str(), name.c_str());
+}
+
+inline Value *Object::FindUInt(Value &val, const char *memberId) {
+    return FindUIntInContext(val, memberId, id.c_str(), name.c_str());
+}
+
+inline Value *Object::FindArray(Value &val, const char *memberId) {
+    return FindArrayInContext(val, memberId, id.c_str(), name.c_str());
+}
+
+inline Value *Object::FindObject(Value &val, const char *memberId) {
+    return FindObjectInContext(val, memberId, id.c_str(), name.c_str());
+}
+
+inline Value *Object::FindExtension(Value &val, const char *extensionId) {
+    return FindExtensionInContext(val, extensionId, id.c_str(), name.c_str());
+}
+
 #ifdef ASSIMP_ENABLE_DRACO
 
 template <typename T>
@@ -349,17 +441,20 @@ inline LazyDict<T>::~LazyDict() {
 template <class T>
 inline void LazyDict<T>::AttachToDocument(Document &doc) {
     Value *container = nullptr;
+    const char* context = nullptr;
 
     if (mExtId) {
         if (Value *exts = FindObject(doc, "extensions")) {
-            container = FindObject(*exts, mExtId);
+            container = FindObjectInContext(*exts, mExtId, "extensions");
+            context = mExtId;
         }
     } else {
         container = &doc;
+        context = "the document";
     }
 
     if (container) {
-        mDict = FindArray(*container, mDictId);
+        mDict = FindArrayInContext(*container, mDictId, context);
     }
 }
 
@@ -383,6 +478,7 @@ unsigned int LazyDict<T>::Remove(const char *id) {
     mAsset.mUsedIds[id] = false;
     mObjsById.erase(id);
     mObjsByOIndex.erase(index);
+    delete mObjs[index];
     mObjs.erase(mObjs.begin() + index);
 
     //update index of object in mObjs;
@@ -788,7 +884,14 @@ inline void Accessor::Read(Value &obj, Asset &r) {
 
     byteOffset = MemberOrDefault(obj, "byteOffset", size_t(0));
     componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE);
-    count = MemberOrDefault(obj, "count", size_t(0));
+    {
+        const Value* countValue = FindUInt(obj, "count");
+        if (!countValue || countValue->GetInt() < 1)
+        {
+            throw DeadlyImportError("A strictly positive count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")");
+        }
+        count = countValue->GetUint();
+    }
 
     const char *typestr;
     type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR;
@@ -1107,8 +1210,7 @@ inline void Texture::Read(Value &obj, Asset &r) {
     }
 }
 
-namespace {
-inline void SetTextureProperties(Asset &r, Value *prop, TextureInfo &out) {
+void Material::SetTextureProperties(Asset &r, Value *prop, TextureInfo &out) {
     if (r.extensionsUsed.KHR_texture_transform) {
         if (Value *pKHR_texture_transform = FindExtension(*prop, "KHR_texture_transform")) {
             out.textureTransformSupported = true;
@@ -1134,8 +1236,8 @@ inline void SetTextureProperties(Asset &r, Value *prop, TextureInfo &out) {
         }
     }
 
-    if (Value *index = FindUInt(*prop, "index")) {
-        out.texture = r.textures.Retrieve(index->GetUint());
+    if (Value *indexProp = FindUInt(*prop, "index")) {
+        out.texture = r.textures.Retrieve(indexProp->GetUint());
     }
 
     if (Value *texcoord = FindUInt(*prop, "texCoord")) {
@@ -1143,13 +1245,13 @@ inline void SetTextureProperties(Asset &r, Value *prop, TextureInfo &out) {
     }
 }
 
-inline void ReadTextureProperty(Asset &r, Value &vals, const char *propName, TextureInfo &out) {
+inline void Material::ReadTextureProperty(Asset &r, Value &vals, const char *propName, TextureInfo &out) {
     if (Value *prop = FindMember(vals, propName)) {
         SetTextureProperties(r, prop, out);
     }
 }
 
-inline void ReadTextureProperty(Asset &r, Value &vals, const char *propName, NormalTextureInfo &out) {
+inline void Material::ReadTextureProperty(Asset &r, Value &vals, const char *propName, NormalTextureInfo &out) {
     if (Value *prop = FindMember(vals, propName)) {
         SetTextureProperties(r, prop, out);
 
@@ -1159,7 +1261,7 @@ inline void ReadTextureProperty(Asset &r, Value &vals, const char *propName, Nor
     }
 }
 
-inline void ReadTextureProperty(Asset &r, Value &vals, const char *propName, OcclusionTextureInfo &out) {
+inline void Material::ReadTextureProperty(Asset &r, Value &vals, const char *propName, OcclusionTextureInfo &out) {
     if (Value *prop = FindMember(vals, propName)) {
         SetTextureProperties(r, prop, out);
 
@@ -1168,7 +1270,6 @@ inline void ReadTextureProperty(Asset &r, Value &vals, const char *propName, Occ
         }
     }
 }
-} // namespace
 
 inline void Material::Read(Value &material, Asset &r) {
     SetDefaults();
@@ -1762,17 +1863,10 @@ inline void AssetMetadata::Read(Document &doc) {
         ReadMember(*obj, "copyright", copyright);
         ReadMember(*obj, "generator", generator);
 
-        if (Value *versionString = FindString(*obj, "version")) {
+        if (Value *versionString = FindStringInContext(*obj, "version", "\"asset\"")) {
             version = versionString->GetString();
-        } else if (Value *versionNumber = FindNumber(*obj, "version")) {
-            char buf[4];
-
-            ai_snprintf(buf, 4, "%.1f", versionNumber->GetDouble());
-
-            version = buf;
         }
-
-        Value *curProfile = FindObject(*obj, "profile");
+        Value *curProfile = FindObjectInContext(*obj, "profile", "\"asset\"");
         if (nullptr != curProfile) {
             ReadMember(*curProfile, "api", this->profile.api);
             ReadMember(*curProfile, "version", this->profile.version);

+ 14 - 13
code/AssetLib/glTF2/glTF2AssetWriter.inl

@@ -110,19 +110,18 @@ namespace glTF2 {
         if (a.bufferView) {
             obj.AddMember("bufferView", a.bufferView->index, w.mAl);
             obj.AddMember("byteOffset", (unsigned int)a.byteOffset, w.mAl);
-            Value vTmpMax, vTmpMin;
-            if (a.componentType == ComponentType_FLOAT) {
-                obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl);
-                obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl);
-            } else {
-                obj.AddMember("max", MakeValueCast<int64_t>(vTmpMax, a.max, w.mAl), w.mAl);
-                obj.AddMember("min", MakeValueCast<int64_t>(vTmpMin, a.min, w.mAl), w.mAl);
-            }
         }
-
         obj.AddMember("componentType", int(a.componentType), w.mAl);
         obj.AddMember("count", (unsigned int)a.count, w.mAl);
         obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl);
+        Value vTmpMax, vTmpMin;
+        if (a.componentType == ComponentType_FLOAT) {
+            obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl);
+            obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl);
+        } else {
+            obj.AddMember("max", MakeValueCast<int64_t>(vTmpMax, a.max, w.mAl), w.mAl);
+            obj.AddMember("min", MakeValueCast<int64_t>(vTmpMin, a.min, w.mAl), w.mAl);
+        }
 
         if (a.sparse) {
             Value sparseValue;
@@ -887,6 +886,7 @@ namespace glTF2 {
         if (d.mObjs.empty()) return;
 
         Value* container = &mDoc;
+		const char* context = "Document";
 
         if (d.mExtId) {
             Value* exts = FindObject(mDoc, "extensions");
@@ -895,17 +895,18 @@ namespace glTF2 {
                 exts = FindObject(mDoc, "extensions");
             }
 
-            container = FindObject(*exts, d.mExtId);
+            container = FindObjectInContext(*exts, d.mExtId, "extensions");
             if (nullptr != container) {
                 exts->AddMember(StringRef(d.mExtId), Value().SetObject().Move(), mDoc.GetAllocator());
-                container = FindObject(*exts, d.mExtId);
+                container = FindObjectInContext(*exts, d.mExtId, "extensions");
+                context = d.mExtId;
             }
         }
 
-        Value *dict = FindArray(*container, d.mDictId);
+        Value *dict = FindArrayInContext(*container, d.mDictId, context);
         if (nullptr == dict) {
             container->AddMember(StringRef(d.mDictId), Value().SetArray().Move(), mDoc.GetAllocator());
-            dict = FindArray(*container, d.mDictId);
+            dict = FindArrayInContext(*container, d.mDictId, context);
             if (nullptr == dict) {
                 return;
             }

+ 3 - 3
code/PostProcessing/ComputeUVMappingProcess.cpp

@@ -122,7 +122,7 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
         const aiFace& face = mesh->mFaces[fidx];
         if (face.mNumIndices < 3) continue; // triangles and polygons only, please
 
-        unsigned int small = face.mNumIndices, large = small;
+        unsigned int smallV = face.mNumIndices, large = smallV;
         bool zero = false, one = false, round_to_zero = false;
 
         // Check whether this face lies on a UV seam. We can just guess,
@@ -133,7 +133,7 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
         {
             if (out[face.mIndices[n]].x < LOWER_LIMIT)
             {
-                small = n;
+                smallV = n;
 
                 // If we have a U value very close to 0 we can't
                 // round the others to 0, too.
@@ -151,7 +151,7 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
                     one = true;
             }
         }
-        if (small != face.mNumIndices && large != face.mNumIndices)
+        if (smallV != face.mNumIndices && large != face.mNumIndices)
         {
             for (unsigned int n = 0; n < face.mNumIndices;++n)
             {

+ 1 - 1
contrib/gtest/include/gtest/internal/gtest-internal.h

@@ -1,4 +1,4 @@
-// Copyright 2005, Google Inc.
+// Copyright 2005, Google Inc.
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without

+ 4 - 0
contrib/unzip/crypt.c

@@ -21,6 +21,10 @@
    NOCRYPT and NOUNCRYPT.
 */
 
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
+    #define MICROSOFT_WINDOWS_WINBASE_H_DEFINE_INTERLOCKED_CPLUSPLUS_OVERLOADS 0
+#endif
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>

+ 8 - 0
include/assimp/Exceptional.h

@@ -75,6 +75,10 @@ public:
     template<typename... T>
     explicit DeadlyImportError(T&&... args) :
             DeadlyErrorBase(Assimp::Formatter::format(), std::forward<T>(args)...) {}
+
+#if defined(_MSC_VER) && defined(__clang__)
+    DeadlyImportError(DeadlyImportError& other) = delete;
+#endif
 };
 
 class ASSIMP_API DeadlyExportError : public DeadlyErrorBase {
@@ -83,6 +87,10 @@ public:
     template<typename... T>
     explicit DeadlyExportError(T&&... args) :
             DeadlyErrorBase(Assimp::Formatter::format(), std::forward<T>(args)...) {}
+
+#if defined(_MSC_VER) && defined(__clang__)
+    DeadlyExportError(DeadlyExportError& other) = delete;
+#endif
 };
 
 #ifdef _MSC_VER

+ 8 - 0
include/assimp/importerdesc.h

@@ -52,6 +52,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <assimp/types.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /** Mixed set of flags for #aiImporterDesc, indicating some features
   *  common to many importers*/
 enum aiImporterFlags {
@@ -143,4 +147,8 @@ Will return a nullptr if no assigned importer desc. was found for the given exte
 */
 ASSIMP_API const C_STRUCT aiImporterDesc *aiGetImporterDesc(const char *extension);
 
+#ifdef __cplusplus
+} // end of extern "C"
+#endif
+
 #endif // AI_IMPORTER_DESC_H_INC

+ 1 - 1
include/assimp/metadata.h

@@ -351,7 +351,7 @@ struct aiMetadata {
             *static_cast<T *>(mValues[index].mData) = value;
         } else {
             if (nullptr != mValues[index].mData) {
-                delete mValues[index].mData;
+                delete static_cast<T *>(mValues[index].mData);
                 mValues[index].mData = nullptr;
             }
             mValues[index].mData = new T(value);

BIN
test/models/glTF2/wrongTypes/BoxTextured0.bin


BIN
test/models/glTF2/wrongTypes/CesiumLogoFlat.png


+ 179 - 0
test/models/glTF2/wrongTypes/badArray.gltf

@@ -0,0 +1,179 @@
+{
+    "asset": {
+        "generator": "COLLADA2GLTF",
+        "version": "2.0"
+    },
+    "scene": 0,
+    "scenes": [
+        {
+            "nodes": [
+                0
+            ]
+        }
+    ],
+    "nodes": [
+        {
+            "children": [
+                1
+            ],
+            "matrix": [
+                1.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                -1.0,
+                0.0,
+                0.0,
+                1.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                1.0
+            ]
+        },
+        {
+            "mesh": 0
+        }
+    ],
+    "meshes": [
+        {
+            "primitives": {
+				"not an array": {
+					"NORMAL": 1,
+					"POSITION": 2,
+					"TEXCOORD_0": 3
+				},
+				"indices": 0,
+				"mode": 4,
+				"material": 0
+            },
+            "name": "Mesh"
+        }
+    ],
+    "accessors": [
+        {
+            "bufferView": 0,
+            "byteOffset": 0,
+            "componentType": 5123,
+            "count": 36,
+            "max": [
+                23
+            ],
+            "min": [
+                0
+            ],
+            "type": "SCALAR"
+        },
+        {
+            "bufferView": 1,
+            "byteOffset": 0,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                1.0,
+                1.0,
+                1.0
+            ],
+            "min": [
+                -1.0,
+                -1.0,
+                -1.0
+            ],
+            "type": "VEC3"
+        },
+        {
+            "bufferView": 1,
+            "byteOffset": 288,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                0.5,
+                0.5,
+                0.5
+            ],
+            "min": [
+                -0.5,
+                -0.5,
+                -0.5
+            ],
+            "type": "VEC3"
+        },
+        {
+            "bufferView": 2,
+            "byteOffset": 0,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                6.0,
+                1.0
+            ],
+            "min": [
+                0.0,
+                0.0
+            ],
+            "type": "VEC2"
+        }
+    ],
+    "materials": [
+        {
+            "pbrMetallicRoughness": {
+                "baseColorTexture": {
+                    "index": 0
+                },
+                "metallicFactor": 0.0
+            },
+            "name": "Texture"
+        }
+    ],
+    "textures": [
+        {
+            "sampler": 0,
+            "source": 0
+        }
+    ],
+    "images": [
+        {
+            "uri": "CesiumLogoFlat.png"
+        }
+    ],
+    "samplers": [
+        {
+            "magFilter": 9729,
+            "minFilter": 9986,
+            "wrapS": 33648,
+            "wrapT": 33071
+        }
+    ],
+    "bufferViews": [
+        {
+            "buffer": 0,
+            "byteOffset": 768,
+            "byteLength": 72,
+            "target": 34963
+        },
+        {
+            "buffer": 0,
+            "byteOffset": 0,
+            "byteLength": 576,
+            "byteStride": 12,
+            "target": 34962
+        },
+        {
+            "buffer": 0,
+            "byteOffset": 576,
+            "byteLength": 192,
+            "byteStride": 8,
+            "target": 34962
+        }
+    ],
+    "buffers": [
+        {
+            "byteLength": 840,
+            "uri": "BoxTextured0.bin"
+        }
+    ]
+}

+ 185 - 0
test/models/glTF2/wrongTypes/badExtension.gltf

@@ -0,0 +1,185 @@
+{
+    "asset": {
+        "generator": "COLLADA2GLTF",
+        "version": "2.0"
+    },
+	"extensionsUsed": [ "KHR_texture_transform" ],
+    "scene": 0,
+    "scenes": [
+        {
+            "nodes": [
+                0
+            ]
+        }
+    ],
+    "nodes": [
+        {
+            "children": [
+                1
+            ],
+            "matrix": [
+                1.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                -1.0,
+                0.0,
+                0.0,
+                1.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                1.0
+            ]
+        },
+        {
+            "mesh": 0
+        }
+    ],
+    "meshes": [
+        {
+            "primitives": [
+                {
+                    "attributes": {
+                        "NORMAL": 1,
+                        "POSITION": 2,
+                        "TEXCOORD_0": 3
+                    },
+                    "indices": 0,
+                    "mode": 4,
+                    "material": 0
+                }
+            ],
+            "name": "Mesh"
+        }
+    ],
+    "accessors": [
+        {
+            "bufferView": 0,
+            "byteOffset": 0,
+            "componentType": 5123,
+            "count": 36,
+            "max": [
+                23
+            ],
+            "min": [
+                0
+            ],
+            "type": "SCALAR"
+        },
+        {
+            "bufferView": 1,
+            "byteOffset": 0,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                1.0,
+                1.0,
+                1.0
+            ],
+            "min": [
+                -1.0,
+                -1.0,
+                -1.0
+            ],
+            "type": "VEC3"
+        },
+        {
+            "bufferView": 1,
+            "byteOffset": 288,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                0.5,
+                0.5,
+                0.5
+            ],
+            "min": [
+                -0.5,
+                -0.5,
+                -0.5
+            ],
+            "type": "VEC3"
+        },
+        {
+            "bufferView": 2,
+            "byteOffset": 0,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                6.0,
+                1.0
+            ],
+            "min": [
+                0.0,
+                0.0
+            ],
+            "type": "VEC2"
+        }
+    ],
+    "materials": [
+        {
+            "pbrMetallicRoughness": {
+                "baseColorTexture": {
+                    "index": 0,					
+					"extensions": {
+						"KHR_texture_transform": "Not an object"
+					}
+                },
+                "metallicFactor": 0.0
+            },
+            "name": "Texture"
+        }
+    ],
+    "textures": [
+        {
+            "sampler": 0,
+            "source": 0
+        }
+    ],
+    "images": [
+        {
+            "uri": "CesiumLogoFlat.png"
+        }
+    ],
+    "samplers": [
+        {
+            "magFilter": 9729,
+            "minFilter": 9986,
+            "wrapS": 33648,
+            "wrapT": 33071
+        }
+    ],
+    "bufferViews": [
+        {
+            "buffer": 0,
+            "byteOffset": 768,
+            "byteLength": 72,
+            "target": 34963
+        },
+        {
+            "buffer": 0,
+            "byteOffset": 0,
+            "byteLength": 576,
+            "byteStride": 12,
+            "target": 34962
+        },
+        {
+            "buffer": 0,
+            "byteOffset": 576,
+            "byteLength": 192,
+            "byteStride": 8,
+            "target": 34962
+        }
+    ],
+    "buffers": [
+        {
+            "byteLength": 840,
+            "uri": "BoxTextured0.bin"
+        }
+    ]
+}

+ 184 - 0
test/models/glTF2/wrongTypes/badNumber.gltf

@@ -0,0 +1,184 @@
+{
+    "asset": {
+        "generator": "COLLADA2GLTF",
+        "version": "2.0"
+    },
+    "scene": 0,
+    "scenes": [
+        {
+            "nodes": [
+                0
+            ]
+        }
+    ],
+    "nodes": [
+        {
+            "children": [
+                1
+            ],
+            "matrix": [
+                1.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                -1.0,
+                0.0,
+                0.0,
+                1.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                1.0
+            ]
+        },
+        {
+            "mesh": 0
+        }
+    ],
+    "meshes": [
+        {
+            "primitives": [
+                {
+                    "attributes": {
+                        "NORMAL": 1,
+                        "POSITION": 2,
+                        "TEXCOORD_0": 3
+                    },
+                    "indices": 0,
+                    "mode": 4,
+                    "material": 0
+                }
+            ],
+            "name": "Mesh"
+        }
+    ],
+    "accessors": [
+        {
+            "bufferView": 0,
+            "byteOffset": 0,
+            "componentType": 5123,
+            "count": 36,
+            "max": [
+                23
+            ],
+            "min": [
+                0
+            ],
+            "type": "SCALAR"
+        },
+        {
+            "bufferView": 1,
+            "byteOffset": 0,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                1.0,
+                1.0,
+                1.0
+            ],
+            "min": [
+                -1.0,
+                -1.0,
+                -1.0
+            ],
+            "type": "VEC3"
+        },
+        {
+            "bufferView": 1,
+            "byteOffset": 288,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                0.5,
+                0.5,
+                0.5
+            ],
+            "min": [
+                -0.5,
+                -0.5,
+                -0.5
+            ],
+            "type": "VEC3"
+        },
+        {
+            "bufferView": 2,
+            "byteOffset": 0,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                6.0,
+                1.0
+            ],
+            "min": [
+                0.0,
+                0.0
+            ],
+            "type": "VEC2"
+        }
+    ],
+    "materials": [
+        {
+            "pbrMetallicRoughness": {
+                "baseColorTexture": {
+                    "index": 0
+                },
+                "metallicFactor": 0.0
+            },
+			"normalTexture": {
+				"scale": "not a number"
+			},
+            "name": "Texture"
+        }
+    ],
+    "textures": [
+        {
+            "sampler": 0,
+            "source": 0
+        }
+    ],
+    "images": [
+        {
+            "uri": "CesiumLogoFlat.png"
+        }
+    ],
+    "samplers": [
+        {
+            "magFilter": 9729,
+            "minFilter": 9986,
+            "wrapS": 33648,
+            "wrapT": 33071
+        }
+    ],
+    "bufferViews": [
+        {
+            "buffer": 0,
+            "byteOffset": 768,
+            "byteLength": 72,
+            "target": 34963
+        },
+        {
+            "buffer": 0,
+            "byteOffset": 0,
+            "byteLength": 576,
+            "byteStride": 12,
+            "target": 34962
+        },
+        {
+            "buffer": 0,
+            "byteOffset": 576,
+            "byteLength": 192,
+            "byteStride": 8,
+            "target": 34962
+        }
+    ],
+    "buffers": [
+        {
+            "byteLength": 840,
+            "uri": "BoxTextured0.bin"
+        }
+    ]
+}

+ 176 - 0
test/models/glTF2/wrongTypes/badObject.gltf

@@ -0,0 +1,176 @@
+{
+    "asset": {
+        "generator": "COLLADA2GLTF",
+        "version": "2.0"
+    },
+    "scene": 0,
+    "scenes": [
+        {
+            "nodes": [
+                0
+            ]
+        }
+    ],
+    "nodes": [
+        {
+            "children": [
+                1
+            ],
+            "matrix": [
+                1.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                -1.0,
+                0.0,
+                0.0,
+                1.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                1.0
+            ]
+        },
+        {
+            "mesh": 0
+        }
+    ],
+    "meshes": [
+        {
+            "primitives": [
+                {
+                    "attributes": {
+                        "NORMAL": 1,
+                        "POSITION": 2,
+                        "TEXCOORD_0": 3
+                    },
+                    "indices": 0,
+                    "mode": 4,
+                    "material": 0
+                }
+            ],
+            "name": "Mesh"
+        }
+    ],
+    "accessors": [
+        {
+            "bufferView": 0,
+            "byteOffset": 0,
+            "componentType": 5123,
+            "count": 36,
+            "max": [
+                23
+            ],
+            "min": [
+                0
+            ],
+            "type": "SCALAR"
+        },
+        {
+            "bufferView": 1,
+            "byteOffset": 0,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                1.0,
+                1.0,
+                1.0
+            ],
+            "min": [
+                -1.0,
+                -1.0,
+                -1.0
+            ],
+            "type": "VEC3"
+        },
+        {
+            "bufferView": 1,
+            "byteOffset": 288,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                0.5,
+                0.5,
+                0.5
+            ],
+            "min": [
+                -0.5,
+                -0.5,
+                -0.5
+            ],
+            "type": "VEC3"
+        },
+        {
+            "bufferView": 2,
+            "byteOffset": 0,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                6.0,
+                1.0
+            ],
+            "min": [
+                0.0,
+                0.0
+            ],
+            "type": "VEC2"
+        }
+    ],
+    "materials": [
+        {
+            "pbrMetallicRoughness": ["not an object"],
+            "name": "Texture"
+        }
+    ],
+    "textures": [
+        {
+            "sampler": 0,
+            "source": 0
+        }
+    ],
+    "images": [
+        {
+            "uri": "CesiumLogoFlat.png"
+        }
+    ],
+    "samplers": [
+        {
+            "magFilter": 9729,
+            "minFilter": 9986,
+            "wrapS": 33648,
+            "wrapT": 33071
+        }
+    ],
+    "bufferViews": [
+        {
+            "buffer": 0,
+            "byteOffset": 768,
+            "byteLength": 72,
+            "target": 34963
+        },
+        {
+            "buffer": 0,
+            "byteOffset": 0,
+            "byteLength": 576,
+            "byteStride": 12,
+            "target": 34962
+        },
+        {
+            "buffer": 0,
+            "byteOffset": 576,
+            "byteLength": 192,
+            "byteStride": 8,
+            "target": 34962
+        }
+    ],
+    "buffers": [
+        {
+            "byteLength": 840,
+            "uri": "BoxTextured0.bin"
+        }
+    ]
+}

+ 182 - 0
test/models/glTF2/wrongTypes/badString.gltf

@@ -0,0 +1,182 @@
+{
+    "asset": {
+        "generator": "COLLADA2GLTF",
+        "version": "2.0"
+    },
+    "scene": 0,
+    "scenes": [
+        {
+            "nodes": [
+                0
+            ],
+			"name" : 42
+        }
+    ],
+    "nodes": [
+        {
+            "children": [
+                1
+            ],
+            "matrix": [
+                1.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                -1.0,
+                0.0,
+                0.0,
+                1.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                1.0
+            ]
+        },
+        {
+            "mesh": 0
+        }
+    ],
+    "meshes": [
+        {
+            "primitives": [
+                {
+                    "attributes": {
+                        "NORMAL": 1,
+                        "POSITION": 2,
+                        "TEXCOORD_0": 3
+                    },
+                    "indices": 0,
+                    "mode": 4,
+                    "material": 0
+                }
+            ],
+            "name": "Mesh"
+        }
+    ],
+    "accessors": [
+        {
+            "bufferView": 0,
+            "byteOffset": 0,
+            "componentType": 5123,
+            "count": 36,
+            "max": [
+                23
+            ],
+            "min": [
+                0
+            ],
+            "type": "SCALAR"
+        },
+        {
+            "bufferView": 1,
+            "byteOffset": 0,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                1.0,
+                1.0,
+                1.0
+            ],
+            "min": [
+                -1.0,
+                -1.0,
+                -1.0
+            ],
+            "type": "VEC3"
+        },
+        {
+            "bufferView": 1,
+            "byteOffset": 288,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                0.5,
+                0.5,
+                0.5
+            ],
+            "min": [
+                -0.5,
+                -0.5,
+                -0.5
+            ],
+            "type": "VEC3"
+        },
+        {
+            "bufferView": 2,
+            "byteOffset": 0,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                6.0,
+                1.0
+            ],
+            "min": [
+                0.0,
+                0.0
+            ],
+            "type": "VEC2"
+        }
+    ],
+    "materials": [
+        {
+            "pbrMetallicRoughness": {
+                "baseColorTexture": {
+                    "index": 0
+                },
+                "metallicFactor": 0.0
+            },
+            "name": "Texture"
+        }
+    ],
+    "textures": [
+        {
+            "sampler": 0,
+            "source": 0
+        }
+    ],
+    "images": [
+        {
+            "uri": "CesiumLogoFlat.png"
+        }
+    ],
+    "samplers": [
+        {
+            "magFilter": 9729,
+            "minFilter": 9986,
+            "wrapS": 33648,
+            "wrapT": 33071
+        }
+    ],
+    "bufferViews": [
+        {
+            "buffer": 0,
+            "byteOffset": 768,
+            "byteLength": 72,
+            "target": 34963
+        },
+        {
+            "buffer": 0,
+            "byteOffset": 0,
+            "byteLength": 576,
+            "byteStride": 12,
+            "target": 34962
+        },
+        {
+            "buffer": 0,
+            "byteOffset": 576,
+            "byteLength": 192,
+            "byteStride": 8,
+            "target": 34962
+        }
+    ],
+    "buffers": [
+        {
+            "byteLength": 840,
+            "uri": "BoxTextured0.bin"
+        }
+    ]
+}

+ 182 - 0
test/models/glTF2/wrongTypes/badUint.gltf

@@ -0,0 +1,182 @@
+{
+    "asset": {
+        "generator": "COLLADA2GLTF",
+        "version": "2.0"
+    },
+    "scene": 0,
+    "scenes": [
+        {
+            "nodes": [
+                0
+            ],
+			"name" : "hello"
+        }
+    ],
+    "nodes": [
+        {
+            "children": [
+                1
+            ],
+            "matrix": [
+                1.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                -1.0,
+                0.0,
+                0.0,
+                1.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                0.0,
+                1.0
+            ]
+        },
+        {
+            "mesh": 0
+        }
+    ],
+    "meshes": [
+        {
+            "primitives": [
+                {
+                    "attributes": {
+                        "NORMAL": 1,
+                        "POSITION": 2,
+                        "TEXCOORD_0": 3
+                    },
+                    "indices": 0,
+                    "mode": 4,
+                    "material": 0
+                }
+            ],
+            "name": "Mesh"
+        }
+    ],
+    "accessors": [
+        {
+            "bufferView": 0,
+            "byteOffset": 0,
+            "componentType": 5123,
+            "count": 36,
+            "max": [
+                23
+            ],
+            "min": [
+                0
+            ],
+            "type": "SCALAR"
+        },
+        {
+            "bufferView": 1,
+            "byteOffset": 0,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                1.0,
+                1.0,
+                1.0
+            ],
+            "min": [
+                -1.0,
+                -1.0,
+                -1.0
+            ],
+            "type": "VEC3"
+        },
+        {
+            "bufferView": 1,
+            "byteOffset": 288,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                0.5,
+                0.5,
+                0.5
+            ],
+            "min": [
+                -0.5,
+                -0.5,
+                -0.5
+            ],
+            "type": "VEC3"
+        },
+        {
+            "bufferView": 2,
+            "byteOffset": 0,
+            "componentType": 5126,
+            "count": 24,
+            "max": [
+                6.0,
+                1.0
+            ],
+            "min": [
+                0.0,
+                0.0
+            ],
+            "type": "VEC2"
+        }
+    ],
+    "materials": [
+        {
+            "pbrMetallicRoughness": {
+                "baseColorTexture": {
+                    "index": -1
+                },
+                "metallicFactor": 0.0
+            },
+            "name": "Texture"
+        }
+    ],
+    "textures": [
+        {
+            "sampler": 0,
+            "source": 0
+        }
+    ],
+    "images": [
+        {
+            "uri": "CesiumLogoFlat.png"
+        }
+    ],
+    "samplers": [
+        {
+            "magFilter": 9729,
+            "minFilter": 9986,
+            "wrapS": 33648,
+            "wrapT": 33071
+        }
+    ],
+    "bufferViews": [
+        {
+            "buffer": 0,
+            "byteOffset": 768,
+            "byteLength": 72,
+            "target": 34963
+        },
+        {
+            "buffer": 0,
+            "byteOffset": 0,
+            "byteLength": 576,
+            "byteStride": 12,
+            "target": 34962
+        },
+        {
+            "buffer": 0,
+            "byteOffset": 576,
+            "byteLength": 192,
+            "byteStride": 8,
+            "target": 34962
+        }
+    ],
+    "buffers": [
+        {
+            "byteLength": 840,
+            "uri": "BoxTextured0.bin"
+        }
+    ]
+}

+ 25 - 0
test/unit/utglTF2ImportExport.cpp

@@ -609,3 +609,28 @@ TEST_F(utglTF2ImportExport, import_dracoEncoded) {
 #endif
 }
 
+TEST_F(utglTF2ImportExport, wrongTypes) {
+    // Deliberately broken version of the BoxTextured.gltf asset.
+    std::vector<std::tuple<std::string, std::string, std::string, std::string>> wrongTypes = {
+        { "/glTF2/wrongTypes/badArray.gltf", "array", "primitives", "meshes[0]" },
+        { "/glTF2/wrongTypes/badString.gltf", "string", "name", "scenes[0]" },
+        { "/glTF2/wrongTypes/badUint.gltf", "uint", "index", "materials[0]" },
+        { "/glTF2/wrongTypes/badNumber.gltf", "number", "scale", "materials[0]" },
+        { "/glTF2/wrongTypes/badObject.gltf", "object", "pbrMetallicRoughness", "materials[0]" },
+        { "/glTF2/wrongTypes/badExtension.gltf", "object", "KHR_texture_transform", "materials[0]" }
+    };
+    for (const auto& tuple : wrongTypes)
+    {
+        const auto& file = std::get<0>(tuple);
+        const auto& type = std::get<1>(tuple);
+        const auto& member = std::get<2>(tuple);
+        const auto& context = std::get<3>(tuple);
+        Assimp::Importer importer;
+        const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR + file , aiProcess_ValidateDataStructure);
+        ASSERT_EQ(scene, nullptr);
+        const std::string error = importer.GetErrorString();
+        EXPECT_FALSE(error.empty());
+        EXPECT_NE(error.find(member + "\" was not of type \"" + type + "\" when reading " + context), std::string::npos);
+    }
+}
+

+ 20 - 0
tools/assimp_cmd/Main.cpp

@@ -325,6 +325,14 @@ bool ExportModel(const aiScene* pOut,
 		PrintHorBar();
 	}
 
+	aiMatrix4x4 rx, ry, rz;
+    aiMatrix4x4::RotationX(imp.rot.x, rx);
+    aiMatrix4x4::RotationY(imp.rot.y, ry);
+    aiMatrix4x4::RotationZ(imp.rot.z, rz);
+	pOut->mRootNode->mTransformation *= rx;
+    pOut->mRootNode->mTransformation *= ry;
+    pOut->mRootNode->mTransformation *= rz;
+
 	// do the actual export, measure time
 	const clock_t first = clock();
 	const aiReturn res = globalExporter->Export(pOut,pID,path);
@@ -493,6 +501,18 @@ int ProcessStandardArguments(
 		else if (! strcmp( param, "-v") || ! strcmp( param, "--verbose")) {
 			fill.verbose = true;
 		}
+		else if (!strncmp(params[i], "-rx=", 4) || !strncmp(params[i], "--rotation-x=", 13)) {
+            std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4));
+            fill.rot.x = std::stof(value);
+		} 
+		else if (!strncmp(params[i], "-ry=", 4) || !strncmp(params[i], "--rotation-y=", 13)) {
+            std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4));
+            fill.rot.y = std::stof(value);
+        } 
+		else if (!strncmp(params[i], "-rz=", 4) || !strncmp(params[i], "--rotation-z=", 13)) {
+            std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4));
+            fill.rot.z = std::stof(value);
+        }
 		else if (! strncmp( param, "--log-out=",10) || ! strncmp( param, "-lo",3)) {
 			fill.logFile = std::string(params[i]+(params[i][1] == '-' ? 10 : 3));
 			if (!fill.logFile.length()) {

+ 4 - 0
tools/assimp_cmd/Main.h

@@ -96,6 +96,7 @@ struct ImportData {
 		,	showLog (false)
 		,	verbose (false)
 		,	log	    (false)
+        ,   rot     (aiVector3D(0.f, 0.f, 0.f))
 	{}
 
 	/// Post-processing flags
@@ -112,6 +113,9 @@ struct ImportData {
 
 	// Need to log?
 	bool log;
+
+	// Export With Rotation
+	aiVector3D rot;
 };
 
 /// \enum AssimpCmdError