diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index c597fc951..bdc62dbc2 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -376,6 +376,51 @@ struct CustomExtension { } }; +struct ExtrasValue; + +//! Represents a union of metadata values +struct ExtrasValue { + std::string name; + + Nullable mBoolValue; + Nullable mInt32Value; + Nullable mUint64Value; + Nullable mFloatValue; + Nullable mDoubleValue; + Nullable mStringValue; + Nullable> mMetadataValue; + + ExtrasValue() = default; + ~ExtrasValue() = default; + + ExtrasValue(const ExtrasValue& other) : + name(other.name), + mStringValue(other.mStringValue), + mDoubleValue(other.mDoubleValue), + mUint64Value(other.mUint64Value), + mInt32Value(other.mInt32Value), + mBoolValue(other.mBoolValue), + mMetadataValue(other.mMetadataValue) { + } +}; + +//! Represents metadata in an glTF object +struct Extras { + std::vector mValues; + + inline bool HasExtras() const { + return mValues.size() != 0; + } + + Extras() = default; + ~Extras() = default; + + Extras(const Extras &other) : + mValues(other.mValues) { + // empty + } +}; + //! Base class for all glTF top-level objects struct Object { int index; //!< The index of this object within its property container @@ -384,7 +429,7 @@ struct Object { std::string name; //!< The user-defined name of this object CustomExtension customExtensions; - CustomExtension extras; + Extras extras; //! Objects marked as special are not exported (used to emulate the binary body buffer) virtual bool IsSpecial() const { return false; } diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index db47915d6..5dc51e7fa 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -139,6 +139,50 @@ inline CustomExtension ReadExtensions(const char *name, Value &obj) { return ret; } +inline ExtrasValue ReadExtrasValue(const char *name, Value &obj) { + ExtrasValue ret; + ret.name = name; + + if (obj.IsObject()) { + ret.mMetadataValue.value.reserve(obj.MemberCount()); + ret.mMetadataValue.isPresent = true; + for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { + auto &val = it->value; + ret.mMetadataValue.value.push_back(ReadExtrasValue(it->name.GetString(), val)); + } + } else if (obj.IsNumber()) { + if (obj.IsUint64()) { + ret.mUint64Value.value = obj.GetUint64(); + ret.mUint64Value.isPresent = true; + } else if (obj.IsInt()) { + ret.mInt32Value.value = obj.GetInt64(); + ret.mInt32Value.isPresent = true; + } else if (obj.IsDouble()) { + ret.mDoubleValue.value = obj.GetDouble(); + ret.mDoubleValue.isPresent = true; + } + } else if (obj.IsString()) { + ReadValue(obj, ret.mStringValue); + ret.mStringValue.isPresent = true; + } else if (obj.IsBool()) { + ret.mBoolValue.value = obj.GetBool(); + ret.mBoolValue.isPresent = true; + } + return ret; +} + +inline Extras ReadExtras(Value &obj) { + Extras ret; + + ret.mValues.reserve(obj.MemberCount()); + for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { + auto &val = it->value; + ret.mValues.push_back(ReadExtrasValue(it->name.GetString(), val)); + } + + return ret; +} + inline void CopyData(size_t count, const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride) { if (src_stride == dst_stride) { @@ -248,7 +292,7 @@ inline void Object::ReadExtensions(Value &val) { inline void Object::ReadExtras(Value &val) { if (Value *curExtras = FindObject(val, "extras")) { - this->extras = glTF2::ReadExtensions("extras", *curExtras); + this->extras = glTF2::ReadExtras(*curExtras); } } diff --git a/code/AssetLib/glTF2/glTF2AssetWriter.inl b/code/AssetLib/glTF2/glTF2AssetWriter.inl index 0be139595..e1fcaefd4 100644 --- a/code/AssetLib/glTF2/glTF2AssetWriter.inl +++ b/code/AssetLib/glTF2/glTF2AssetWriter.inl @@ -620,6 +620,44 @@ namespace glTF2 { } } + inline void WriteExtrasValue(Value &parent, const ExtrasValue &value, AssetWriter &w) { + Value valueNode; + + if (value.mStringValue.isPresent) { + MakeValue(valueNode, value.mStringValue.value.c_str(), w.mAl); + } else if (value.mDoubleValue.isPresent) { + MakeValue(valueNode, value.mDoubleValue.value, w.mAl); + } else if (value.mUint64Value.isPresent) { + MakeValue(valueNode, value.mUint64Value.value, w.mAl); + } else if (value.mInt32Value.isPresent) { + MakeValue(valueNode, value.mInt32Value.value, w.mAl); + } else if (value.mBoolValue.isPresent) { + MakeValue(valueNode, value.mBoolValue.value, w.mAl); + } else if (value.mMetadataValue.isPresent) { + valueNode.SetObject(); + for (auto const &subvalue : value.mMetadataValue.value) { + WriteExtrasValue(valueNode, subvalue, w); + } + } + + parent.AddMember(StringRef(value.name), valueNode, w.mAl); + } + + inline void WriteExtras(Value &obj, const Extras &extras, AssetWriter &w) { + if (!extras.HasExtras()) { + return; + } + + Value extrasNode; + extrasNode.SetObject(); + + for (auto const &value : extras.mValues) { + WriteExtrasValue(extrasNode, value, w); + } + + obj.AddMember("extras", extrasNode, w.mAl); + } + inline void Write(Value& obj, Node& n, AssetWriter& w) { if (n.matrix.isPresent) { @@ -655,6 +693,8 @@ namespace glTF2 { if(n.skeletons.size()) { AddRefsVector(obj, "skeletons", n.skeletons, w.mAl); } + + WriteExtras(obj, n.extras, w); } inline void Write(Value& /*obj*/, Program& /*b*/, AssetWriter& /*w*/) @@ -728,7 +768,6 @@ namespace glTF2 { } } - inline AssetWriter::AssetWriter(Asset& a) : mDoc() , mAsset(a) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index da7591d52..f8b1995d9 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -445,6 +445,57 @@ inline Ref ExportData(Asset &a, std::string &meshName, Ref &bu return acc; } +inline void ExportNodeExtras(const aiMetadataEntry &metadataEntry, aiString name, ExtrasValue &value) { + + value.name = name.C_Str(); + switch (metadataEntry.mType) { + case AI_BOOL: + value.mBoolValue.value = *static_cast(metadataEntry.mData); + value.mBoolValue.isPresent = true; + break; + case AI_INT32: + value.mInt32Value.value = *static_cast(metadataEntry.mData); + value.mInt32Value.isPresent = true; + break; + case AI_UINT64: + value.mUint64Value.value = *static_cast(metadataEntry.mData); + value.mUint64Value.isPresent = true; + break; + case AI_FLOAT: + value.mFloatValue.value = *static_cast(metadataEntry.mData); + value.mFloatValue.isPresent = true; + break; + case AI_DOUBLE: + value.mDoubleValue.value = *static_cast(metadataEntry.mData); + value.mDoubleValue.isPresent = true; + break; + case AI_AISTRING: + value.mStringValue.value = static_cast(metadataEntry.mData)->C_Str(); + value.mStringValue.isPresent = true; + break; + case AI_AIMETADATA: + const aiMetadata *subMetadata = static_cast(metadataEntry.mData); + value.mMetadataValue.value.resize(subMetadata->mNumProperties); + value.mMetadataValue.isPresent = true; + + for (unsigned i = 0; i < subMetadata->mNumProperties; ++i) { + ExportNodeExtras(subMetadata->mValues[i], subMetadata->mKeys[i], value.mMetadataValue.value.at(i)); + } + break; + } +} + +inline void ExportNodeExtras(const aiMetadata *metadata, Extras &extras) { + if (metadata == nullptr) { + return; + } + + extras.mValues.resize(metadata->mNumProperties); + for (unsigned int i = 0; i < metadata->mNumProperties; ++i) { + ExportNodeExtras(metadata->mValues[i], metadata->mKeys[i], extras.mValues.at(i)); + } +} + inline void SetSamplerWrap(SamplerWrap &wrap, aiTextureMapMode map) { switch (map) { case aiTextureMapMode_Clamp: @@ -1344,7 +1395,7 @@ unsigned int glTF2Exporter::ExportNodeHierarchy(const aiNode *n) { return node.GetIndex(); } -/* + /* * Export node and recursively calls ExportNode for all children. * Since these nodes are not the root node, we also export the parent Ref */ @@ -1355,6 +1406,10 @@ unsigned int glTF2Exporter::ExportNode(const aiNode *n, Ref &parent) { node->parent = parent; node->name = name; + if (n->mMetaData != nullptr && n->mMetaData->mNumProperties > 0) { + ExportNodeExtras(n->mMetaData, node->extras); + } + if (!n->mTransformation.IsIdentity()) { if (mScene->mNumAnimations > 0 || (mProperties && mProperties->HasPropertyBool("GLTF2_NODE_IN_TRS"))) { aiQuaternion quaternion; diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 293d3dea7..a14e6ad6f 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1033,11 +1033,33 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) { } } -void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) { - if (extension.mValues.isPresent) { - for (auto const &subExtension : extension.mValues.value) { - ParseExtensions(metadata, subExtension); +void ParseExtrasValue(aiMetadata *metadata, const ExtrasValue &value) { + + if (value.mBoolValue.isPresent) { + metadata->Add(value.name, value.mBoolValue.value); + } else if (value.mInt32Value.isPresent) { + metadata->Add(value.name, value.mInt32Value.value); + } else if (value.mUint64Value.isPresent) { + metadata->Add(value.name, value.mUint64Value.value); + } else if (value.mFloatValue.isPresent) { + metadata->Add(value.name, value.mFloatValue.value); + } else if (value.mDoubleValue.isPresent) { + metadata->Add(value.name, value.mDoubleValue.value); + } else if (value.mStringValue.isPresent) { + metadata->Add(value.name, aiString(value.mStringValue.value)); + } else if (value.mMetadataValue.isPresent) { + aiMetadata subMetadata; + for (auto const &subValue : value.mMetadataValue.value) { + ParseExtrasValue(&subMetadata, subValue); } + + metadata->Add(value.name, subMetadata); + } +} + +void ParseExtras(aiMetadata* metadata, const Extras& extras) { + for (auto const &value : extras.mValues) { + ParseExtrasValue(metadata, value); } } @@ -1059,12 +1081,12 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & } } - if (node.customExtensions || node.extras) { + if (node.customExtensions || !node.extras.HasExtras()) { ainode->mMetaData = new aiMetadata; if (node.customExtensions) { ParseExtensions(ainode->mMetaData, node.customExtensions); } - if (node.extras) { + if (node.extras.HasExtras()) { ParseExtras(ainode->mMetaData, node.extras); } } diff --git a/code/Common/SceneCombiner.cpp b/code/Common/SceneCombiner.cpp index 2c2539e54..1d32436c2 100644 --- a/code/Common/SceneCombiner.cpp +++ b/code/Common/SceneCombiner.cpp @@ -1349,6 +1349,9 @@ void SceneCombiner::Copy(aiMetadata **_dest, const aiMetadata *src) { case AI_AIVECTOR3D: out.mData = new aiVector3D(*static_cast(in.mData)); break; + case AI_AIMETADATA: + out.mData = new aiMetadata(*static_cast(in.mData)); + break; default: ai_assert(false); break;