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.
pull/3298/head
Evangel 2020-06-26 14:26:45 +10:00
parent 8a57d5df40
commit f6b4370f6a
4 changed files with 140 additions and 5 deletions

View File

@ -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<std::string> mStringValue;
Nullable<double> mDoubleValue;
Nullable<uint64_t> mUint64Value;
Nullable<int64_t> mInt64Value;
// std::vector<CustomExtension> handles both Object and Array
Nullable<std::vector<CustomExtension>> 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<Ref<Node>> children;
std::vector<Ref<Mesh>> meshes;
@ -802,6 +844,8 @@ struct Node : public Object {
Ref<Node> parent; //!< This is not part of the glTF specification. Used as a helper.
CustomExtension extensions;
Node() {}
void Read(Value &obj, Asset &r);
};

View File

@ -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");

View File

@ -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<int32_t>(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<unsigned int> &meshOffsets, glTF2::Ref<glTF2::Node> &ptr) {
Node &node = *ptr;
@ -863,6 +881,11 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
}
}
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<unsigned int> &
//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);
}
}
}

View File

@ -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 <string>
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<aiVector3D>(mKeys[i], v);
mValues[i].mData = new aiVector3D(v);
} break;
case AI_AIMETADATA: {
aiMetadata v;
rhs.Get<aiMetadata>(mKeys[i], v);
mValues[i].mData = new aiMetadata(v);
} break;
#ifndef SWIG
case FORCE_32BIT:
#endif
@ -213,6 +224,14 @@ 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.
*/
@ -245,6 +264,9 @@ struct aiMetadata {
case AI_AIVECTOR3D:
delete static_cast<aiVector3D *>(data);
break;
case AI_AIMETADATA:
delete static_cast<aiMetadata *>(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<T *>(mValues[index].mData) = value;
} else {
mValues[index].mData = new T(value);
}