Jelajahi Sumber

Merge pull request #3298 from Evangel63/recursive_metadata

Added arbitrary recursive metadata to allow for glTF2's extensions to…
Kim Kulling 5 tahun lalu
induk
melakukan
45531df9aa

+ 46 - 0
code/AssetLib/glTF2/glTF2Asset.h

@@ -784,6 +784,50 @@ 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;
+    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 {
     std::vector<Ref<Node>> children;
     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.
 
+    CustomExtension extensions;
+
     Node() {}
     void Read(Value &obj, Asset &r);
 };

+ 43 - 0
code/AssetLib/glTF2/glTF2Asset.inl

@@ -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) {
     if (name.empty()) {
         name = id;
@@ -1261,6 +1302,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");

+ 32 - 2
code/AssetLib/glTF2/glTF2Importer.cpp

@@ -847,6 +847,26 @@ static std::string GetNodeName(const Node &node) {
 	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) {
 	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);
 
 	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
 		//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);
+			}
 		}
 	}
 

+ 110 - 3
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 <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,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<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);
         }
@@ -418,6 +442,89 @@ struct aiMetadata {
         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
 };
 

+ 10 - 1
test/unit/utMetadata.cpp

@@ -197,9 +197,11 @@ TEST_F( utMetadata, copy_test ) {
     m_data->Set( 5, "aiString", strVal );
     aiVector3D vecVal( 1, 2, 3 );
     m_data->Set( 6, "aiVector3D", vecVal );
+    aiMetadata metaVal;
+    m_data->Set( 7, "aiMetadata", metaVal );
 
     aiMetadata copy( *m_data );
-    EXPECT_EQ( 7u, copy.mNumProperties );
+    EXPECT_EQ( 8u, copy.mNumProperties );
 
     // bool test
     {
@@ -251,4 +253,11 @@ TEST_F( utMetadata, copy_test ) {
         EXPECT_TRUE( copy.Get( "aiVector3D", v ) );
         EXPECT_EQ( vecVal, v );
     }
+
+    // metadata test
+    {
+        aiMetadata v;
+        EXPECT_TRUE( copy.Get( "aiMetadata", v ) );
+        EXPECT_EQ( metaVal, v );
+    }
 }