From f6b4370f6ac1bf2db0ef9edeb0ff1ce8c7e61aab Mon Sep 17 00:00:00 2001 From: Evangel Date: Fri, 26 Jun 2020 14:26:45 +1000 Subject: [PATCH] Added arbitrary recursive metadata to allow for glTF2's extensions to be properly represented. Primary changes are to include/assimp/metadata.h, adding in the aiMetadata GetAiType function, adding the operator= to allow an aiMetadata type to be assigned, adding a check for the AI_AIMETADATA type flag as it can't be trivially memcpy'd. operator= is implemented with a by-value argument as then the copy is made by the copy constructor and we can just swap everything out and let the destructor handle the mess. Implemented parsing of the "extensions" flag on all glTF2 Nodes. Doesn't use the ReadValue helper function on numbers as it did not seem to fill out the Nullable structure properly. --- code/AssetLib/glTF2/glTF2Asset.h | 44 +++++++++++++++++++++++++++ code/AssetLib/glTF2/glTF2Asset.inl | 39 ++++++++++++++++++++++++ code/AssetLib/glTF2/glTF2Importer.cpp | 32 +++++++++++++++++-- include/assimp/metadata.h | 30 ++++++++++++++++-- 4 files changed, 140 insertions(+), 5 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index ffecd3dbf..543bef95c 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -784,6 +784,48 @@ struct Mesh : public Object { 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 mStringValue; + Nullable mDoubleValue; + Nullable mUint64Value; + Nullable mInt64Value; + + // std::vector handles both Object and Array + Nullable> mValues; + + operator bool() const { + return Size(); + } + + size_t Size() const { + if (mValues.isPresent) { + return mValues.value.size(); + } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.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) + , mValues(other.mValues) + { + } +}; + struct Node : public Object { std::vector> children; std::vector> meshes; @@ -802,6 +844,8 @@ struct Node : public Object { Ref parent; //!< This is not part of the glTF specification. Used as a helper. + CustomExtension extensions; + Node() {} void Read(Value &obj, Asset &r); }; diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 5a99525b2..71b164149 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1207,6 +1207,43 @@ 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; + } + return ret; +} + inline void Node::Read(Value &obj, Asset &r) { if (name.empty()) { name = id; @@ -1261,6 +1298,8 @@ inline void Node::Read(Value &obj, Asset &r) { Value *curExtensions = FindObject(obj, "extensions"); if (nullptr != curExtensions) { + this->extensions = ReadExtensions("extensions", *curExtensions); + if (r.extensionsUsed.KHR_lights_punctual) { if (Value *ext = FindObject(*curExtensions, "KHR_lights_punctual")) { Value *curLight = FindUInt(*ext, "light"); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 1c044e7ce..3a3c66e15 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -847,6 +847,24 @@ static std::string GetNodeName(const Node &node) { return node.name.empty() ? node.id : node.name; } +void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension, unsigned int depth = 0) { + if (extension.mStringValue.isPresent) { + metadata->Add(extension.name.c_str(), aiString(extension.mStringValue.value)); + } else if (extension.mDoubleValue.isPresent) { + metadata->Add(extension.name.c_str(), extension.mDoubleValue.value); + } else if (extension.mUint64Value.isPresent) { + metadata->Add(extension.name.c_str(), extension.mUint64Value.value); + } else if (extension.mInt64Value.isPresent) { + metadata->Add(extension.name.c_str(), static_cast(extension.mInt64Value.value)); + } else if (extension.mValues.isPresent) { + aiMetadata val; + for (size_t i = 0; i < extension.mValues.value.size(); ++i) { + ParseExtensions(&val, extension.mValues.value[i], depth + 2); + } + metadata->Add(extension.name.c_str(), val); + } +} + aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector &meshOffsets, glTF2::Ref &ptr) { Node &node = *ptr; @@ -863,6 +881,11 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & } } + if (node.extensions) { + ainode->mMetaData = new aiMetadata; + ParseExtensions(ainode->mMetaData, node.extensions); + } + GetNodeTransform(ainode->mTransformation, node); if (!node.meshes.empty()) { @@ -957,8 +980,13 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & //range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual //it is added to meta data of parent node, because there is no other place to put it if (node.light->range.isPresent) { - ainode->mMetaData = aiMetadata::Alloc(1); - ainode->mMetaData->Set(0, "PBR_LightRange", node.light->range.value); + if (!ainode->mMetaData) { + ainode->mMetaData = aiMetadata::Alloc(1); + ainode->mMetaData->Set(0, "PBR_LightRange", node.light->range.value); + } + else { + ainode->mMetaData->Add("PBR_LightRange", node.light->range.value); + } } } diff --git a/include/assimp/metadata.h b/include/assimp/metadata.h index 684508a71..95d919584 100644 --- a/include/assimp/metadata.h +++ b/include/assimp/metadata.h @@ -69,7 +69,8 @@ typedef enum aiMetadataType { AI_DOUBLE = 4, AI_AISTRING = 5, AI_AIVECTOR3D = 6, - AI_META_MAX = 7, + AI_AIMETADATA = 7, + AI_META_MAX = 8, #ifndef SWIG FORCE_32BIT = INT_MAX @@ -100,6 +101,8 @@ struct aiMetadataEntry { #include +struct aiMetadata; + // ------------------------------------------------------------------------------- /** * Helper functions to get the aiType enum entry for a type @@ -127,6 +130,9 @@ inline aiMetadataType GetAiType(const aiString &) { inline aiMetadataType GetAiType(const aiVector3D &) { return AI_AIVECTOR3D; } +inline aiMetadataType GetAiType(const aiMetadata &) { + return AI_AIMETADATA; +} #endif // __cplusplus @@ -204,6 +210,11 @@ struct aiMetadata { rhs.Get(mKeys[i], v); mValues[i].mData = new aiVector3D(v); } break; + case AI_AIMETADATA: { + aiMetadata v; + rhs.Get(mKeys[i], v); + mValues[i].mData = new aiMetadata(v); + } break; #ifndef SWIG case FORCE_32BIT: #endif @@ -213,7 +224,15 @@ struct aiMetadata { } } - /** + aiMetadata &operator=(aiMetadata rhs) { + using std::swap; + swap(mNumProperties, rhs.mNumProperties); + swap(mKeys, rhs.mKeys); + swap(mValues, rhs.mValues); + return *this; + } + + /** * @brief The destructor. */ ~aiMetadata() { @@ -245,6 +264,9 @@ struct aiMetadata { case AI_AIVECTOR3D: delete static_cast(data); break; + case AI_AIMETADATA: + delete static_cast(data); + break; #ifndef SWIG case FORCE_32BIT: #endif @@ -323,8 +345,10 @@ struct aiMetadata { mValues[index].mType = GetAiType(value); // Copy the given value to the dynamic storage - if (nullptr != mValues[index].mData) { + if (nullptr != mValues[index].mData && AI_AIMETADATA != mValues[index].mType) { ::memcpy(mValues[index].mData, &value, sizeof(T)); + } else if (nullptr != mValues[index].mData && AI_AIMETADATA == mValues[index].mType) { + *static_cast(mValues[index].mData) = value; } else { mValues[index].mData = new T(value); }