Merge pull request #3298 from Evangel63/recursive_metadata

Added arbitrary recursive metadata to allow for glTF2's extensions to…
pull/3299/head
Kim Kulling 2020-06-27 15:52:16 +02:00 committed by GitHub
commit 45531df9aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 241 additions and 6 deletions

View File

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

View File

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

View File

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

View File

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

View File

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