Merge pull request #3298 from Evangel63/recursive_metadata
Added arbitrary recursive metadata to allow for glTF2's extensions to…pull/3299/head
commit
45531df9aa
|
@ -784,6 +784,50 @@ struct Mesh : public Object {
|
||||||
void Read(Value &pJSON_Object, Asset &pAsset_Root);
|
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;
|
||||||
|
Nullable<bool> mBoolValue;
|
||||||
|
|
||||||
|
// 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 || mBoolValue.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)
|
||||||
|
, mBoolValue(other.mBoolValue)
|
||||||
|
, mValues(other.mValues)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct Node : public Object {
|
struct Node : public Object {
|
||||||
std::vector<Ref<Node>> children;
|
std::vector<Ref<Node>> children;
|
||||||
std::vector<Ref<Mesh>> meshes;
|
std::vector<Ref<Mesh>> meshes;
|
||||||
|
@ -802,6 +846,8 @@ struct Node : public Object {
|
||||||
|
|
||||||
Ref<Node> parent; //!< This is not part of the glTF specification. Used as a helper.
|
Ref<Node> parent; //!< This is not part of the glTF specification. Used as a helper.
|
||||||
|
|
||||||
|
CustomExtension extensions;
|
||||||
|
|
||||||
Node() {}
|
Node() {}
|
||||||
void Read(Value &obj, Asset &r);
|
void Read(Value &obj, Asset &r);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1207,6 +1207,47 @@ 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;
|
||||||
|
}
|
||||||
|
else if (obj.IsBool()) {
|
||||||
|
ret.mBoolValue.value = obj.GetBool();
|
||||||
|
ret.mBoolValue.isPresent = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
inline void Node::Read(Value &obj, Asset &r) {
|
inline void Node::Read(Value &obj, Asset &r) {
|
||||||
if (name.empty()) {
|
if (name.empty()) {
|
||||||
name = id;
|
name = id;
|
||||||
|
@ -1261,6 +1302,8 @@ inline void Node::Read(Value &obj, Asset &r) {
|
||||||
|
|
||||||
Value *curExtensions = FindObject(obj, "extensions");
|
Value *curExtensions = FindObject(obj, "extensions");
|
||||||
if (nullptr != curExtensions) {
|
if (nullptr != curExtensions) {
|
||||||
|
this->extensions = ReadExtensions("extensions", *curExtensions);
|
||||||
|
|
||||||
if (r.extensionsUsed.KHR_lights_punctual) {
|
if (r.extensionsUsed.KHR_lights_punctual) {
|
||||||
if (Value *ext = FindObject(*curExtensions, "KHR_lights_punctual")) {
|
if (Value *ext = FindObject(*curExtensions, "KHR_lights_punctual")) {
|
||||||
Value *curLight = FindUInt(*ext, "light");
|
Value *curLight = FindUInt(*ext, "light");
|
||||||
|
|
|
@ -847,6 +847,26 @@ static std::string GetNodeName(const Node &node) {
|
||||||
return node.name.empty() ? node.id : node.name;
|
return node.name.empty() ? node.id : node.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) {
|
||||||
|
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.mBoolValue.isPresent) {
|
||||||
|
metadata->Add(extension.name.c_str(), extension.mBoolValue.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]);
|
||||||
|
}
|
||||||
|
metadata->Add(extension.name.c_str(), val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &meshOffsets, glTF2::Ref<glTF2::Node> &ptr) {
|
aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &meshOffsets, glTF2::Ref<glTF2::Node> &ptr) {
|
||||||
Node &node = *ptr;
|
Node &node = *ptr;
|
||||||
|
|
||||||
|
@ -863,6 +883,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);
|
GetNodeTransform(ainode->mTransformation, node);
|
||||||
|
|
||||||
if (!node.meshes.empty()) {
|
if (!node.meshes.empty()) {
|
||||||
|
@ -957,8 +982,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
|
//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
|
//it is added to meta data of parent node, because there is no other place to put it
|
||||||
if (node.light->range.isPresent) {
|
if (node.light->range.isPresent) {
|
||||||
ainode->mMetaData = aiMetadata::Alloc(1);
|
if (!ainode->mMetaData) {
|
||||||
ainode->mMetaData->Set(0, "PBR_LightRange", node.light->range.value);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,8 @@ typedef enum aiMetadataType {
|
||||||
AI_DOUBLE = 4,
|
AI_DOUBLE = 4,
|
||||||
AI_AISTRING = 5,
|
AI_AISTRING = 5,
|
||||||
AI_AIVECTOR3D = 6,
|
AI_AIVECTOR3D = 6,
|
||||||
AI_META_MAX = 7,
|
AI_AIMETADATA = 7,
|
||||||
|
AI_META_MAX = 8,
|
||||||
|
|
||||||
#ifndef SWIG
|
#ifndef SWIG
|
||||||
FORCE_32BIT = INT_MAX
|
FORCE_32BIT = INT_MAX
|
||||||
|
@ -100,6 +101,8 @@ struct aiMetadataEntry {
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
struct aiMetadata;
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* Helper functions to get the aiType enum entry for a type
|
* 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 &) {
|
inline aiMetadataType GetAiType(const aiVector3D &) {
|
||||||
return AI_AIVECTOR3D;
|
return AI_AIVECTOR3D;
|
||||||
}
|
}
|
||||||
|
inline aiMetadataType GetAiType(const aiMetadata &) {
|
||||||
|
return AI_AIMETADATA;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
@ -204,6 +210,11 @@ struct aiMetadata {
|
||||||
rhs.Get<aiVector3D>(mKeys[i], v);
|
rhs.Get<aiVector3D>(mKeys[i], v);
|
||||||
mValues[i].mData = new aiVector3D(v);
|
mValues[i].mData = new aiVector3D(v);
|
||||||
} break;
|
} break;
|
||||||
|
case AI_AIMETADATA: {
|
||||||
|
aiMetadata v;
|
||||||
|
rhs.Get<aiMetadata>(mKeys[i], v);
|
||||||
|
mValues[i].mData = new aiMetadata(v);
|
||||||
|
} break;
|
||||||
#ifndef SWIG
|
#ifndef SWIG
|
||||||
case FORCE_32BIT:
|
case FORCE_32BIT:
|
||||||
#endif
|
#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.
|
* @brief The destructor.
|
||||||
*/
|
*/
|
||||||
|
@ -245,6 +264,9 @@ struct aiMetadata {
|
||||||
case AI_AIVECTOR3D:
|
case AI_AIVECTOR3D:
|
||||||
delete static_cast<aiVector3D *>(data);
|
delete static_cast<aiVector3D *>(data);
|
||||||
break;
|
break;
|
||||||
|
case AI_AIMETADATA:
|
||||||
|
delete static_cast<aiMetadata *>(data);
|
||||||
|
break;
|
||||||
#ifndef SWIG
|
#ifndef SWIG
|
||||||
case FORCE_32BIT:
|
case FORCE_32BIT:
|
||||||
#endif
|
#endif
|
||||||
|
@ -323,8 +345,10 @@ struct aiMetadata {
|
||||||
mValues[index].mType = GetAiType(value);
|
mValues[index].mType = GetAiType(value);
|
||||||
|
|
||||||
// Copy the given value to the dynamic storage
|
// 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));
|
::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 {
|
} else {
|
||||||
mValues[index].mData = new T(value);
|
mValues[index].mData = new T(value);
|
||||||
}
|
}
|
||||||
|
@ -418,6 +442,89 @@ struct aiMetadata {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
friend bool CompareKeys(const aiMetadata &lhs, const aiMetadata &rhs) {
|
||||||
|
if (lhs.mNumProperties != rhs.mNumProperties) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < lhs.mNumProperties; ++i) {
|
||||||
|
if (lhs.mKeys[i] != rhs.mKeys[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool CompareValues(const aiMetadata &lhs, const aiMetadata &rhs) {
|
||||||
|
if (lhs.mNumProperties != rhs.mNumProperties) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < lhs.mNumProperties; ++i) {
|
||||||
|
if (lhs.mValues[i].mType != rhs.mValues[i].mType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (lhs.mValues[i].mType) {
|
||||||
|
case AI_BOOL: {
|
||||||
|
if (*static_cast<bool *>(lhs.mValues[i].mData) != *static_cast<bool *>(rhs.mValues[i].mData)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case AI_INT32: {
|
||||||
|
if (*static_cast<int32_t *>(lhs.mValues[i].mData) != *static_cast<int32_t *>(rhs.mValues[i].mData)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case AI_UINT64: {
|
||||||
|
if (*static_cast<uint64_t *>(lhs.mValues[i].mData) != *static_cast<uint64_t *>(rhs.mValues[i].mData)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case AI_FLOAT: {
|
||||||
|
if (*static_cast<float *>(lhs.mValues[i].mData) != *static_cast<float *>(rhs.mValues[i].mData)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case AI_DOUBLE: {
|
||||||
|
if (*static_cast<double *>(lhs.mValues[i].mData) != *static_cast<double *>(rhs.mValues[i].mData)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case AI_AISTRING: {
|
||||||
|
if (*static_cast<aiString *>(lhs.mValues[i].mData) != *static_cast<aiString *>(rhs.mValues[i].mData)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case AI_AIVECTOR3D: {
|
||||||
|
if (*static_cast<aiVector3D *>(lhs.mValues[i].mData) != *static_cast<aiVector3D *>(rhs.mValues[i].mData)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case AI_AIMETADATA: {
|
||||||
|
if (*static_cast<aiMetadata *>(lhs.mValues[i].mData) != *static_cast<aiMetadata *>(rhs.mValues[i].mData)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
#ifndef SWIG
|
||||||
|
case FORCE_32BIT:
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(const aiMetadata &lhs, const aiMetadata &rhs) {
|
||||||
|
return CompareKeys(lhs, rhs) && CompareValues(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const aiMetadata &lhs, const aiMetadata &rhs) {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -197,9 +197,11 @@ TEST_F( utMetadata, copy_test ) {
|
||||||
m_data->Set( 5, "aiString", strVal );
|
m_data->Set( 5, "aiString", strVal );
|
||||||
aiVector3D vecVal( 1, 2, 3 );
|
aiVector3D vecVal( 1, 2, 3 );
|
||||||
m_data->Set( 6, "aiVector3D", vecVal );
|
m_data->Set( 6, "aiVector3D", vecVal );
|
||||||
|
aiMetadata metaVal;
|
||||||
|
m_data->Set( 7, "aiMetadata", metaVal );
|
||||||
|
|
||||||
aiMetadata copy( *m_data );
|
aiMetadata copy( *m_data );
|
||||||
EXPECT_EQ( 7u, copy.mNumProperties );
|
EXPECT_EQ( 8u, copy.mNumProperties );
|
||||||
|
|
||||||
// bool test
|
// bool test
|
||||||
{
|
{
|
||||||
|
@ -251,4 +253,11 @@ TEST_F( utMetadata, copy_test ) {
|
||||||
EXPECT_TRUE( copy.Get( "aiVector3D", v ) );
|
EXPECT_TRUE( copy.Get( "aiVector3D", v ) );
|
||||||
EXPECT_EQ( vecVal, v );
|
EXPECT_EQ( vecVal, v );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// metadata test
|
||||||
|
{
|
||||||
|
aiMetadata v;
|
||||||
|
EXPECT_TRUE( copy.Get( "aiMetadata", v ) );
|
||||||
|
EXPECT_EQ( metaVal, v );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue