Bläddra i källkod

{lyn10535} adding custom property container into SceneAPI (#7913)

* {lyn10535} adding custom property container into SceneAPI

Adding custom property container into SceneAPI's Scene Graph node types
so that UDP metadata can be read in from AssImp

Signed-off-by: Allen Jackson <[email protected]>

* Adding code doc for custom property headers

Signed-off-by: Allen Jackson <[email protected]>

* using aznumeric_cast<> to be platform compatible
updated the test to use AZ::u64

Signed-off-by: Allen Jackson <[email protected]>

* fixing spelling

Signed-off-by: Allen Jackson <[email protected]>
Allen Jackson 3 år sedan
förälder
incheckning
a9eb50e4f9

+ 2 - 0
Code/Tools/SceneAPI/SceneBuilder/DllMain.cpp

@@ -17,6 +17,7 @@
 #include <SceneAPI/SceneBuilder/SceneImporter.h>
 #include <SceneAPI/SceneBuilder/Importers/AssImpBitangentStreamImporter.h>
 #include <SceneAPI/SceneBuilder/Importers/AssImpColorStreamImporter.h>
+#include <SceneAPI/SceneBuilder/Importers/AssImpCustomPropertyImporter.h>
 #include <SceneAPI/SceneBuilder/Importers/AssImpMaterialImporter.h>
 #include <SceneAPI/SceneBuilder/Importers/AssImpMeshImporter.h>
 #include <SceneAPI/SceneBuilder/Importers/AssImpTangentStreamImporter.h>
@@ -62,6 +63,7 @@ namespace AZ
                     g_componentDescriptors.push_back(AssImpBoneImporter::CreateDescriptor());
                     g_componentDescriptors.push_back(AssImpAnimationImporter::CreateDescriptor());
                     g_componentDescriptors.push_back(AssImpBlendShapeImporter::CreateDescriptor());
+                    g_componentDescriptors.push_back(AssImpCustomPropertyImporter::CreateDescriptor());
 
                     for (AZ::ComponentDescriptor* descriptor : g_componentDescriptors)
                     {

+ 137 - 0
Code/Tools/SceneAPI/SceneBuilder/Importers/AssImpCustomPropertyImporter.cpp

@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#include <SceneAPI/SceneBuilder/Importers/AssImpCustomPropertyImporter.h>
+#include <SceneAPI/SceneBuilder/Importers/AssImpImporterUtilities.h>
+
+#include <AzCore/std/smart_ptr/make_shared.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <AzToolsFramework/Debug/TraceContext.h>
+#include <SceneAPI/SceneBuilder/SceneSystem.h>
+#include <SceneAPI/SceneBuilder/Importers/ImporterUtilities.h>
+#include <SceneAPI/SceneBuilder/Importers/Utilities/RenamedNodesMap.h>
+#include <SceneAPI/SceneCore/Utilities/Reporting.h>
+#include <SceneAPI/SceneData/GraphData/CustomPropertyData.h>
+#include <SceneAPI/SDKWrapper/AssImpTypeConverter.h>
+#include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
+#include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
+#include <assimp/scene.h>
+
+namespace AZ::SceneAPI::SceneBuilder
+{
+    const char* s_customPropertiesNodeName = "custom_properties";
+
+    AssImpCustomPropertyImporter::AssImpCustomPropertyImporter()
+    {
+        BindToCall(&AssImpCustomPropertyImporter::ImportCustomProperty);
+    }
+
+    void AssImpCustomPropertyImporter::Reflect(ReflectContext* context)
+    {
+        SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
+        if (serializeContext)
+        {
+            serializeContext->Class<AssImpCustomPropertyImporter, SceneCore::LoadingComponent>()->Version(1);
+        }
+    }
+
+    Events::ProcessingResult AssImpCustomPropertyImporter::ImportCustomProperty(AssImpSceneNodeAppendedContext& context)
+    {
+        AZ_TraceContext("Importer", "customProperty");
+        const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
+
+        if (!currentNode->mMetaData || currentNode->mMetaData->mNumProperties == 0)
+        {
+            return Events::ProcessingResult::Ignored;
+        }
+
+        AZ::SceneData::GraphData::CustomPropertyData::PropertyMap propertyMap;
+
+        for (size_t index = 0; index < currentNode->mMetaData->mNumProperties; ++index)
+        {
+            const aiString* key;
+            const aiMetadataEntry* data;
+            currentNode->mMetaData->Get(index, key, data);
+
+            AZStd::string_view currentNodeName(currentNode->mName.C_Str(), currentNode->mName.length);
+            AZStd::string_view keyName(key->C_Str(), key->length);
+
+            if (data->mType == AI_AISTRING)
+            {
+                const aiString* value = reinterpret_cast<const aiString*>(data->mData);
+                propertyMap[keyName] = AZStd::make_any<AZStd::string>(value->C_Str(), value->length);
+            }
+            else if (data->mType == AI_BOOL)
+            {
+                const bool* value = reinterpret_cast<const bool*>(data->mData);
+                propertyMap[keyName] = AZStd::make_any<bool>(aznumeric_cast<bool>(*value));
+            }
+            else if (data->mType == AI_INT32)
+            {
+                const int32_t* value = reinterpret_cast<const int32_t*>(data->mData);
+                propertyMap[keyName] = AZStd::make_any<AZ::s32>(aznumeric_cast<AZ::s32>(*value));
+            }
+            else if (data->mType == AI_UINT64)
+            {
+                const uint64_t* value = reinterpret_cast<const uint64_t*>(data->mData);
+                propertyMap[keyName] = AZStd::make_any<AZ::u64>(aznumeric_cast<AZ::u64>(*value));
+            }
+            else if (data->mType == AI_FLOAT)
+            {
+                const float* value = reinterpret_cast<const float*>(data->mData);
+                propertyMap[keyName] = AZStd::make_any<float>(aznumeric_cast<float>(*value));
+            }
+            else if (data->mType == AI_DOUBLE)
+            {
+                const double* value = reinterpret_cast<const double*>(data->mData);
+                propertyMap[keyName] = AZStd::make_any<double>(aznumeric_cast<double>(*value));
+            }
+        }
+
+        auto customPropertyMapData = AZStd::make_shared<SceneData::GraphData::CustomPropertyData>(propertyMap);
+        if (!customPropertyMapData)
+        {
+            return Events::ProcessingResult::Failure;
+        }
+
+        // If it is non-endpoint data populated node, add a custom property node attribute
+        if (context.m_scene.GetGraph().HasNodeContent(context.m_currentGraphPosition))
+        {
+            if (!context.m_scene.GetGraph().IsNodeEndPoint(context.m_currentGraphPosition))
+            {
+                AZStd::string nodeName{ s_customPropertiesNodeName };
+                RenamedNodesMap::SanitizeNodeName(nodeName, context.m_scene.GetGraph(), context.m_currentGraphPosition);
+
+                auto newIndex = context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str());
+                AZ_Error(SceneAPI::Utilities::ErrorWindow, newIndex.IsValid(), "Failed to create SceneGraph node for attribute.");
+                if (!newIndex.IsValid())
+                {
+                    return Events::ProcessingResult::Failure;
+                }
+
+                Events::ProcessingResult attributeResult;
+                AssImpSceneAttributeDataPopulatedContext dataPopulated(context, customPropertyMapData, newIndex, nodeName);
+                attributeResult = Events::Process(dataPopulated);
+
+                if (attributeResult != Events::ProcessingResult::Failure)
+                {
+                    attributeResult = AddAttributeDataNodeWithContexts(dataPopulated);
+                }
+
+                return attributeResult;
+            }
+        }
+        else
+        {
+            bool addedData = context.m_scene.GetGraph().SetContent(context.m_currentGraphPosition, customPropertyMapData);
+            AZ_Error(SceneAPI::Utilities::ErrorWindow, addedData, "Failed to add node data");
+            return addedData ? Events::ProcessingResult::Success : Events::ProcessingResult::Failure;
+        }
+
+        return Events::ProcessingResult::Ignored;
+    }
+}

+ 34 - 0
Code/Tools/SceneAPI/SceneBuilder/Importers/AssImpCustomPropertyImporter.h

@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
+#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
+
+namespace AZ::SceneAPI::SceneBuilder
+{
+    //! Stores the string-value from a source scent asset's node; scene builders will be able to access
+    //! the key-value pairs to tweak the scene manifest, create special rules, and produce custom assets
+    //!
+    //! The keys are all AZStd::string
+    //! The supported value types are AZStd::string, bool, int32_t, int64_t, float, and double
+    class AssImpCustomPropertyImporter
+        : public SceneCore::LoadingComponent
+    {
+    public:
+        AZ_COMPONENT(AssImpCustomPropertyImporter, "{BEFF2CA0-CB11-43FF-8BF9-1A58E133186A}", SceneCore::LoadingComponent);
+
+        AssImpCustomPropertyImporter();
+        ~AssImpCustomPropertyImporter() override = default;
+
+        static void Reflect(ReflectContext* context);
+
+        Events::ProcessingResult ImportCustomProperty(AssImpSceneNodeAppendedContext& context);
+    };
+}

+ 2 - 0
Code/Tools/SceneAPI/SceneBuilder/scenebuilder_files.cmake

@@ -32,6 +32,8 @@ set(FILES
     Importers/AssImpBoneImporter.cpp
     Importers/AssImpColorStreamImporter.h
     Importers/AssImpColorStreamImporter.cpp
+    Importers/AssImpCustomPropertyImporter.h
+    Importers/AssImpCustomPropertyImporter.cpp
     Importers/AssImpImporterUtilities.h
     Importers/AssImpImporterUtilities.cpp
     Importers/AssImpMaterialImporter.h

+ 33 - 0
Code/Tools/SceneAPI/SceneCore/DataTypes/GraphData/ICustomPropertyData.h

@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/RTTI/RTTI.h>
+#include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
+
+namespace AZ::SceneAPI::DataTypes
+{
+    //! Interface to access the stored custom property key-value node pairs defined in a source scene asset
+    class ICustomPropertyData
+        : public IGraphObject
+    {
+    public:
+        AZ_RTTI(ICustomPropertyData, "{A524B276-A4C5-496D-A4E5-C219A78DF58D}", IGraphObject);
+
+        //! Custom properties will be coming in as pair of a string key to a supported value type
+        //! The supported value types are AZStd::string, bool, int32_t, int64_t, float, and double
+        using PropertyMap = AZStd::unordered_map<AZStd::string, AZStd::any>;
+
+        ~ICustomPropertyData() override = default;
+        void CloneAttributesFrom([[maybe_unused]] const IGraphObject* sourceObject) override {}
+
+        virtual PropertyMap& GetPropertyMap() = 0;
+        virtual const PropertyMap& GetPropertyMap() const = 0;
+    };
+}

+ 1 - 0
Code/Tools/SceneAPI/SceneCore/scenecore_files.cmake

@@ -17,6 +17,7 @@ set(FILES
     DataTypes/DataTypeUtilities.h
     DataTypes/DataTypeUtilities.inl
     DataTypes/DataTypeUtilities.cpp
+    DataTypes/GraphData/ICustomPropertyData.h
     DataTypes/GraphData/IMeshData.h
     DataTypes/GraphData/IMeshVertexColorData.h
     DataTypes/GraphData/IMeshVertexUVData.h

+ 99 - 0
Code/Tools/SceneAPI/SceneData/GraphData/CustomPropertyData.cpp

@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzCore/Serialization/EditContext.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <SceneAPI/SceneData/GraphData/CustomPropertyData.h>
+#include <AzCore/RTTI/BehaviorContext.h>
+
+namespace AZ::SceneData::GraphData
+{
+    CustomPropertyData::CustomPropertyData(PropertyMap propertyMap)
+        : m_propertyMap(AZStd::move(propertyMap))
+    {
+    }
+
+    void CustomPropertyData::SetPropertyMap(const PropertyMap& propertyMap)
+    {
+        m_propertyMap = propertyMap;
+    }
+
+    CustomPropertyData::PropertyMap& CustomPropertyData::GetPropertyMap()
+    {
+        return m_propertyMap;
+    }
+
+    const CustomPropertyData::PropertyMap& CustomPropertyData::GetPropertyMap() const
+    {
+        return m_propertyMap;
+    }
+
+    void CustomPropertyData::GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const
+    {
+        for (const auto& kvp : m_propertyMap)
+        {
+            if (kvp.second.is<AZStd::string>())
+            {
+                const auto* value = AZStd::any_cast<AZStd::string>(&kvp.second);
+                output.Write(kvp.first.c_str(), value->c_str());
+            }
+            else if (kvp.second.is<bool>())
+            {
+                const auto* value = AZStd::any_cast<bool>(&kvp.second);
+                output.Write(kvp.first.c_str(), *value);
+            }
+            else if (kvp.second.is<int32_t>())
+            {
+                const auto* value = AZStd::any_cast<int32_t>(&kvp.second);
+                output.Write(kvp.first.c_str(), aznumeric_cast<int64_t>(*value));
+            }
+            else if (kvp.second.is<uint64_t>())
+            {
+                const auto* value = AZStd::any_cast<uint64_t>(&kvp.second);
+                output.Write(kvp.first.c_str(), *value);
+            }
+            else if (kvp.second.is<float>())
+            {
+                const auto* value = AZStd::any_cast<float>(&kvp.second);
+                output.Write(kvp.first.c_str(), *value);
+            }
+            else if (kvp.second.is<double>())
+            {
+                const auto* value = AZStd::any_cast<double>(&kvp.second);
+                output.Write(kvp.first.c_str(), *value);
+            }
+        }
+    }
+
+    void CustomPropertyData::Reflect(ReflectContext* context)
+    {
+        SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
+        if (serializeContext)
+        {
+            serializeContext->Class<CustomPropertyData>()->Version(1)
+                ->Field("propertyMap", &CustomPropertyData::m_propertyMap);
+        }
+
+        BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context);
+        if (behaviorContext)
+        {
+            behaviorContext->Class<SceneAPI::DataTypes::ICustomPropertyData>()
+                ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
+                ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
+                ->Attribute(AZ::Script::Attributes::Module, "scene");
+
+            behaviorContext->Class<CustomPropertyData>()
+                ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
+                ->Attribute(AZ::Script::Attributes::Module, "scene")
+                ->Method("GetPropertyMap", [](CustomPropertyData& self) -> const CustomPropertyData::PropertyMap&
+                {
+                    return self.GetPropertyMap();
+                });
+        }
+    }
+}

+ 42 - 0
Code/Tools/SceneAPI/SceneData/GraphData/CustomPropertyData.h

@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <SceneAPI/SceneData/SceneDataConfiguration.h>
+#include <SceneAPI/SceneCore/DataTypes/GraphData/ICustomPropertyData.h>
+
+namespace AZ
+{
+    class ReflectContext;
+
+    namespace SceneData::GraphData
+    {
+        class SCENE_DATA_CLASS CustomPropertyData
+            : public AZ::SceneAPI::DataTypes::ICustomPropertyData
+        {
+        public:
+            using PropertyMap = SceneAPI::DataTypes::ICustomPropertyData::PropertyMap;
+
+            AZ_RTTI(CustomPropertyData, "{19BC99F8-E461-4079-B734-E2628B0B1837}", AZ::SceneAPI::DataTypes::ICustomPropertyData);
+
+            SCENE_DATA_API CustomPropertyData() = default;
+            SCENE_DATA_API explicit CustomPropertyData(PropertyMap properyMap);
+            SCENE_DATA_API virtual void SetPropertyMap(const PropertyMap& properyMap);
+
+            SCENE_DATA_API PropertyMap& GetPropertyMap() override;
+            SCENE_DATA_API const PropertyMap& GetPropertyMap() const override;
+            SCENE_DATA_API void GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const override;
+
+            static void Reflect(ReflectContext* context);
+
+        protected:
+            PropertyMap m_propertyMap;
+        };
+    } // namespace SceneData::GraphData
+} // namespace AZ

+ 3 - 0
Code/Tools/SceneAPI/SceneData/ReflectionRegistrar.cpp

@@ -27,6 +27,7 @@
 #include <SceneAPI/SceneData/GraphData/AnimationData.h>
 #include <SceneAPI/SceneData/GraphData/BlendShapeData.h>
 #include <SceneAPI/SceneData/GraphData/BoneData.h>
+#include <SceneAPI/SceneData/GraphData/CustomPropertyData.h>
 #include <SceneAPI/SceneData/GraphData/MaterialData.h>
 #include <SceneAPI/SceneData/GraphData/MeshData.h>
 #include <SceneAPI/SceneData/GraphData/MeshVertexColorData.h>
@@ -91,6 +92,7 @@ namespace AZ
             context->Class<AZ::SceneData::GraphData::SkinMeshData>()->Version(1);
             context->Class<AZ::SceneData::GraphData::SkinWeightData>()->Version(1);
             AZ::SceneData::GraphData::TransformData::Reflect(context);
+            AZ::SceneData::GraphData::CustomPropertyData::Reflect(context);
         }
 
         void RegisterDataTypeBehaviorReflection(AZ::BehaviorContext* context)
@@ -107,6 +109,7 @@ namespace AZ
             AZ::SceneData::GraphData::AnimationData::Reflect(context);
             AZ::SceneData::GraphData::BlendShapeAnimationData::Reflect(context);
             AZ::SceneData::GraphData::BlendShapeData::Reflect(context);
+            AZ::SceneData::GraphData::CustomPropertyData::Reflect(context);
         }
     } // namespace SceneAPI
 } // namespace AZ

+ 2 - 0
Code/Tools/SceneAPI/SceneData/SceneData_files.cmake

@@ -69,6 +69,8 @@ set(FILES
     Rules/SkinRule.cpp
     Rules/TangentsRule.h
     Rules/TangentsRule.cpp
+    GraphData/CustomPropertyData.h
+    GraphData/CustomPropertyData.cpp
     GraphData/MeshData.h
     GraphData/MeshData.cpp
     GraphData/MeshVertexColorData.h

+ 37 - 2
Code/Tools/SceneAPI/SceneData/Tests/GraphData/GraphDataBehaviorTests.cpp

@@ -21,6 +21,7 @@
 #include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
 #include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
 #include <SceneAPI/SceneData/ReflectionRegistrar.h>
+#include <SceneAPI/SceneData/GraphData/CustomPropertyData.h>
 #include <SceneAPI/SceneData/GraphData/MeshData.h>
 #include <SceneAPI/SceneData/GraphData/MeshVertexBitangentData.h>
 #include <SceneAPI/SceneData/GraphData/MeshVertexColorData.h>
@@ -187,6 +188,18 @@ namespace AZ
                         boneData->SetWorldTransform(SceneAPI::DataTypes::MatrixType::CreateDiagonal({1.0, 2.0, 3.0}));
                         return true;
                     }
+                    else if (data.get_type_info().m_id == azrtti_typeid<AZ::SceneData::GraphData::CustomPropertyData>())
+                    {
+                        AZ::SceneData::GraphData::CustomPropertyData::PropertyMap propertyMap;
+                        propertyMap["a_string"] = AZStd::make_any<AZStd::string>("the string");
+                        propertyMap["a_bool"] = AZStd::make_any<bool>(true);
+                        propertyMap["a_int32"] = AZStd::make_any<int32_t>(aznumeric_cast<int32_t>(-32));
+                        propertyMap["a_uint64"] = AZStd::make_any<AZ::u64>(aznumeric_cast<AZ::u64>(64));
+                        propertyMap["a_float"] = AZStd::make_any<float>(aznumeric_cast<float>(12.34));
+                        propertyMap["a_double"] = AZStd::make_any<double>(aznumeric_cast<double>(0.1234));
+                        AZStd::any_cast<AZ::SceneData::GraphData::CustomPropertyData>(&data)->SetPropertyMap(propertyMap);
+                        return true;
+                    }
                     else if (data.get_type_info().m_id == azrtti_typeid<AZ::SceneData::GraphData::RootBoneData>())
                     {
                         auto* boneData = AZStd::any_cast<AZ::SceneData::GraphData::RootBoneData>(&data);
@@ -238,10 +251,20 @@ namespace AZ
                     EXPECT_EQ(lhs, rhs);
                 }
 
+
+                static void Create()
+                {
+                    AZ::NameDictionary::Create();
+                }
+
+                static void Destroy()
+                {
+                    AZ::NameDictionary::Destroy();
+                }
+
                 void SetUp() override
                 {
                     UnitTest::AllocatorsFixture::SetUp();
-                    AZ::NameDictionary::Create();
 
                     m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
                     AZ::SceneAPI::RegisterDataTypeReflection(m_serializeContext.get());
@@ -264,7 +287,6 @@ namespace AZ
                     m_serializeContext.reset();
                     m_behaviorContext.reset();
 
-                    AZ::NameDictionary::Destroy();
                     UnitTest::AllocatorsFixture::TearDown();
                 }
 
@@ -566,6 +588,19 @@ namespace AZ
                 ExpectExecute("TestExpectFloatEquals(boneData:GetWorldTransform():GetRow(2).w, 0.0)");
             }
 
+            TEST_F(GrapDatahBehaviorScriptTest, SceneGraph_CustomPropertyData_AccessWorks)
+            {
+                ExpectExecute("customPropertyData = CustomPropertyData()");
+                ExpectExecute("TestExpectTrue(customPropertyData ~= nil)");
+                ExpectExecute("MockGraphData.FillData(customPropertyData)");
+                ExpectExecute("TestExpectTrue(customPropertyData:GetPropertyMap():At('a_string') == 'the string')");
+                ExpectExecute("TestExpectTrue(customPropertyData:GetPropertyMap():At('a_bool') == true)");
+                ExpectExecute("TestExpectIntegerEquals(customPropertyData:GetPropertyMap():At('a_int32'), -32)");
+                ExpectExecute("TestExpectIntegerEquals(customPropertyData:GetPropertyMap():At('a_uint64'), 64)");
+                ExpectExecute("TestExpectFloatEquals(customPropertyData:GetPropertyMap():At('a_float'), 12.34)");
+                ExpectExecute("TestExpectFloatEquals(customPropertyData:GetPropertyMap():At('a_double'), 0.1234)");
+            }
+
             TEST_F(GrapDatahBehaviorScriptTest, SceneGraph_RootBoneData_AccessWorks)
             {
                 ExpectExecute("rootBoneData = RootBoneData()");