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