diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index d75251cb2..0e4ba6eda 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/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. diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index a8d4f2d98..77537028f 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/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 +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 @@ -349,17 +441,20 @@ inline LazyDict::~LazyDict() { template inline void LazyDict::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); } } @@ -789,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; @@ -1108,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; @@ -1135,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")) { @@ -1144,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); @@ -1160,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); @@ -1169,7 +1270,6 @@ inline void ReadTextureProperty(Asset &r, Value &vals, const char *propName, Occ } } } -} // namespace inline void Material::Read(Value &material, Asset &r) { SetDefaults(); @@ -1763,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); diff --git a/code/AssetLib/glTF2/glTF2AssetWriter.inl b/code/AssetLib/glTF2/glTF2AssetWriter.inl index 8bcc89e29..166eada0f 100644 --- a/code/AssetLib/glTF2/glTF2AssetWriter.inl +++ b/code/AssetLib/glTF2/glTF2AssetWriter.inl @@ -886,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"); @@ -894,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; } diff --git a/test/models/glTF2/wrongTypes/BoxTextured0.bin b/test/models/glTF2/wrongTypes/BoxTextured0.bin new file mode 100644 index 000000000..d2a73551f Binary files /dev/null and b/test/models/glTF2/wrongTypes/BoxTextured0.bin differ diff --git a/test/models/glTF2/wrongTypes/CesiumLogoFlat.png b/test/models/glTF2/wrongTypes/CesiumLogoFlat.png new file mode 100644 index 000000000..45d502ed2 Binary files /dev/null and b/test/models/glTF2/wrongTypes/CesiumLogoFlat.png differ diff --git a/test/models/glTF2/wrongTypes/badArray.gltf b/test/models/glTF2/wrongTypes/badArray.gltf new file mode 100644 index 000000000..2d12819b4 --- /dev/null +++ b/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" + } + ] +} diff --git a/test/models/glTF2/wrongTypes/badExtension.gltf b/test/models/glTF2/wrongTypes/badExtension.gltf new file mode 100644 index 000000000..01164213b --- /dev/null +++ b/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" + } + ] +} diff --git a/test/models/glTF2/wrongTypes/badNumber.gltf b/test/models/glTF2/wrongTypes/badNumber.gltf new file mode 100644 index 000000000..aa0719ed6 --- /dev/null +++ b/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" + } + ] +} diff --git a/test/models/glTF2/wrongTypes/badObject.gltf b/test/models/glTF2/wrongTypes/badObject.gltf new file mode 100644 index 000000000..98c72f04d --- /dev/null +++ b/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" + } + ] +} diff --git a/test/models/glTF2/wrongTypes/badString.gltf b/test/models/glTF2/wrongTypes/badString.gltf new file mode 100644 index 000000000..7923fcc0a --- /dev/null +++ b/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" + } + ] +} diff --git a/test/models/glTF2/wrongTypes/badUint.gltf b/test/models/glTF2/wrongTypes/badUint.gltf new file mode 100644 index 000000000..63ec98320 --- /dev/null +++ b/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" + } + ] +} diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 1975f65a5..4110edcfc 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/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> 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); + } +} +