diff --git a/Code/Framework/AtomCore/AtomCore/atomcore_files.cmake b/Code/Framework/AtomCore/AtomCore/atomcore_files.cmake index bcfbfb2764..c90263468f 100644 --- a/Code/Framework/AtomCore/AtomCore/atomcore_files.cmake +++ b/Code/Framework/AtomCore/AtomCore/atomcore_files.cmake @@ -13,8 +13,6 @@ set(FILES Instance/InstanceData.h Instance/InstanceData.cpp Instance/InstanceDatabase.h - Serialization/Json/JsonUtils.h - Serialization/Json/JsonUtils.cpp std/containers/array_view.h std/containers/fixed_vector_set.h std/containers/lru_cache.h diff --git a/Code/Framework/AtomCore/Tests/atomcore_tests_files.cmake b/Code/Framework/AtomCore/Tests/atomcore_tests_files.cmake index f2c0c5c051..0f5fcb441d 100644 --- a/Code/Framework/AtomCore/Tests/atomcore_tests_files.cmake +++ b/Code/Framework/AtomCore/Tests/atomcore_tests_files.cmake @@ -10,7 +10,6 @@ set(FILES ArrayView.cpp ConcurrencyCheckerTests.cpp InstanceDatabase.cpp - JsonSerializationUtilsTests.cpp lru_cache.cpp Main.cpp vector_set.cpp diff --git a/Code/Framework/AzCore/AzCore/Component/EntitySerializer.cpp b/Code/Framework/AzCore/AzCore/Component/EntitySerializer.cpp index df6aacd50b..803daf1373 100644 --- a/Code/Framework/AzCore/AzCore/Component/EntitySerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Component/EntitySerializer.cpp @@ -81,9 +81,14 @@ namespace AZ azrtti_typeid(), inputValue, "Components", context); + static TypeId genericComponentWrapperTypeId("{68D358CA-89B9-4730-8BA6-E181DEA28FDE}"); for (auto& [componentKey, component] : componentMap) { - entityInstance->m_components.emplace_back(component); + // if underlying type is genericComponentWrapperTypeId, the template is null and the component should not be addded + if (component->GetUnderlyingComponentType() != genericComponentWrapperTypeId) + { + entityInstance->m_components.emplace_back(component); + } } result.Combine(componentLoadResult); diff --git a/Code/Framework/AzCore/AzCore/Serialization/AZStdAnyDataContainer.inl b/Code/Framework/AzCore/AzCore/Serialization/AZStdAnyDataContainer.inl index 0e9306a481..f0694ed2b3 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/AZStdAnyDataContainer.inl +++ b/Code/Framework/AzCore/AzCore/Serialization/AZStdAnyDataContainer.inl @@ -209,39 +209,35 @@ namespace AZ //! performance sensitive code. AZ_INLINE bool CompareAnyValue(const AZStd::any& lhs, const AZStd::any& rhs) { - bool isEqual = false; - - if (lhs.type() != rhs.type()) + if (lhs.type() == rhs.type()) { - return false; - } + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext); - AZ::SerializeContext* serializeContext = nullptr; - AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext); - - const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(lhs.type()); - if (classData) - { - if (classData->m_serializer) + const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(lhs.type()); + if (classData) { - isEqual = classData->m_serializer->CompareValueData(AZStd::any_cast(&lhs), AZStd::any_cast(&rhs)); - } - else - { - AZStd::vector myData; - AZ::IO::ByteContainerStream myDataStream(&myData); - - AZ::Utils::SaveObjectToStream(myDataStream, AZ::ObjectStream::ST_BINARY, AZStd::any_cast(&lhs), lhs.type()); - - AZStd::vector otherData; - AZ::IO::ByteContainerStream otherDataStream(&otherData); - - AZ::Utils::SaveObjectToStream(otherDataStream, AZ::ObjectStream::ST_BINARY, AZStd::any_cast(&rhs), rhs.type()); - isEqual = (myData.size() == otherData.size()) && (memcmp(myData.data(), otherData.data(), myData.size()) == 0); + if (classData->m_serializer) + { + return classData->m_serializer->CompareValueData(AZStd::any_cast(&lhs), AZStd::any_cast(&rhs)); + } + else + { + AZStd::vector myData; + AZ::IO::ByteContainerStream myDataStream(&myData); + + AZ::Utils::SaveObjectToStream(myDataStream, AZ::ObjectStream::ST_BINARY, AZStd::any_cast(&lhs), lhs.type()); + + AZStd::vector otherData; + AZ::IO::ByteContainerStream otherDataStream(&otherData); + + AZ::Utils::SaveObjectToStream(otherDataStream, AZ::ObjectStream::ST_BINARY, AZStd::any_cast(&rhs), rhs.type()); + return (myData.size() == otherData.size()) && (memcmp(myData.data(), otherData.data(), myData.size()) == 0); + } } } - return isEqual; + return false; } } } diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.cpp index 56933e464a..7309955a1c 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.cpp @@ -213,9 +213,13 @@ namespace AZ void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context, ContinuationFlags flags) { bool loadAsNewInstance = (flags & ContinuationFlags::LoadAsNewInstance) == ContinuationFlags::LoadAsNewInstance; + JsonDeserializer::UseTypeDeserializer useCustom = (flags & ContinuationFlags::IgnoreTypeSerializer) == ContinuationFlags::IgnoreTypeSerializer + ? JsonDeserializer::UseTypeDeserializer::No + : JsonDeserializer::UseTypeDeserializer::Yes; + return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer - ? JsonDeserializer::LoadToPointer(object, typeId, value, context) - : JsonDeserializer::Load(object, typeId, value, loadAsNewInstance, context); + ? JsonDeserializer::LoadToPointer(object, typeId, value, useCustom, context) + : JsonDeserializer::Load(object, typeId, value, loadAsNewInstance, useCustom, context); } JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoring( @@ -224,11 +228,15 @@ namespace AZ { using namespace JsonSerializationResult; + JsonSerializer::UseTypeSerializer useCustom = (flags & ContinuationFlags::IgnoreTypeSerializer) == ContinuationFlags::IgnoreTypeSerializer + ? JsonSerializer::UseTypeSerializer::No + : JsonSerializer::UseTypeSerializer::Yes; + if ((flags & ContinuationFlags::ReplaceDefault) == ContinuationFlags::ReplaceDefault && !context.ShouldKeepDefaults()) { if ((flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer) { - return JsonSerializer::StoreFromPointer(output, object, nullptr, typeId, context); + return JsonSerializer::StoreFromPointer(output, object, nullptr, typeId, useCustom, context); } else { @@ -241,19 +249,19 @@ namespace AZ { return result; } - return result.Combine(JsonSerializer::Store(output, object, nullptr, typeId, context)); + return result.Combine(JsonSerializer::Store(output, object, nullptr, typeId, useCustom, context)); } else { void* defaultObjectPtr = AZStd::any_cast(&newDefaultObject); - return JsonSerializer::Store(output, object, defaultObjectPtr, typeId, context); + return JsonSerializer::Store(output, object, defaultObjectPtr, typeId, useCustom, context); } } } return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer ? - JsonSerializer::StoreFromPointer(output, object, defaultObject, typeId, context) : - JsonSerializer::Store(output, object, defaultObject, typeId, context); + JsonSerializer::StoreFromPointer(output, object, defaultObject, typeId, useCustom, context) : + JsonSerializer::Store(output, object, defaultObject, typeId, useCustom, context); } JsonSerializationResult::ResultCode BaseJsonSerializer::LoadTypeId(Uuid& typeId, const rapidjson::Value& input, diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.h b/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.h index fc35bdcf85..7d2af01c16 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.h @@ -159,12 +159,13 @@ namespace AZ enum class ContinuationFlags { - None = 0, //! No extra flags. - ResolvePointer = 1 << 0, //! The pointer passed in contains a pointer. The (de)serializer will attempt to resolve to an instance. - ReplaceDefault = 1 << 1, //! The default value provided for storing will be replaced with a newly created one. - LoadAsNewInstance = 1 << 2 //! Treats the value as if it's a newly created instance. This may trigger serializers marked with - //! OperationFlags::InitializeNewInstance. Used for instance by pointers or new instances added to - //! an array. + None = 0, //! No extra flags. + ResolvePointer = 1 << 0, //! The pointer passed in contains a pointer. The (de)serializer will attempt to resolve to an instance. + ReplaceDefault = 1 << 1, //! The default value provided for storing will be replaced with a newly created one. + LoadAsNewInstance = 1 << 2, //! Treats the value as if it's a newly created instance. This may trigger serializers marked with + //! OperationFlags::InitializeNewInstance. Used for instance by pointers or new instances added to + //! an array. + IgnoreTypeSerializer = 1 << 3, //! Ignore the custom/specific serializer for the TypeId }; enum class OperationFlags diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp index 840034e52f..324df7c141 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp @@ -38,7 +38,8 @@ namespace AZ } JsonSerializationResult::ResultCode JsonDeserializer::Load( - void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, JsonDeserializerContext& context) + void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, UseTypeDeserializer custom, + JsonDeserializerContext& context) { using namespace AZ::JsonSerializationResult; @@ -48,8 +49,8 @@ namespace AZ "Target object for Json Serialization is pointing to nothing during loading."); } - BaseJsonSerializer* serializer = context.GetRegistrationContext()->GetSerializerForType(typeId); - if (serializer) + if (BaseJsonSerializer* serializer + = (custom == UseTypeDeserializer::Yes ? context.GetRegistrationContext()->GetSerializerForType(typeId) : nullptr)) { return DeserializerDefaultCheck(serializer, object, typeId, value, isNewInstance, context); } @@ -70,8 +71,11 @@ namespace AZ // type itself has not been reflected using EnumBuilder. Treat it as an enum. return LoadEnum(object, *classData, value, context); } - serializer = context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId()); - if (serializer) + + if (BaseJsonSerializer* serializer + = (custom == UseTypeDeserializer::Yes) + ? context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId()) + : nullptr) { return DeserializerDefaultCheck(serializer, object, typeId, value, isNewInstance, context); } @@ -101,7 +105,7 @@ namespace AZ } JsonSerializationResult::ResultCode JsonDeserializer::LoadToPointer(void* object, const Uuid& typeId, - const rapidjson::Value& value, JsonDeserializerContext& context) + const rapidjson::Value& value, UseTypeDeserializer useCustom, JsonDeserializerContext& context) { using namespace JsonSerializationResult; @@ -134,7 +138,7 @@ namespace AZ const SerializeContext::ClassData* resolvedClassData = context.GetSerializeContext()->FindClassData(resolvedTypeId); if (resolvedClassData) { - status = JsonDeserializer::Load(*objectPtr, resolvedTypeId, value, true, context); + status = JsonDeserializer::Load(*objectPtr, resolvedTypeId, value, true, useCustom, context); *objectPtr = resolvedClassData->m_azRtti->Cast(*objectPtr, typeId); @@ -171,11 +175,11 @@ namespace AZ } AZ_Assert(classElement.m_azRtti->GetTypeId() == classElement.m_typeId, "Type id mismatch during deserialization of a json file. (%s vs %s)"); - return LoadToPointer(object, classElement.m_typeId, value, context); + return LoadToPointer(object, classElement.m_typeId, value, UseTypeDeserializer::Yes, context); } else { - return Load(object, classElement.m_typeId, value, false, context); + return Load(object, classElement.m_typeId, value, false, UseTypeDeserializer::Yes, context); } } @@ -571,11 +575,23 @@ namespace AZ if (loadedTypeId.m_determination == TypeIdDetermination::FailedToDetermine || loadedTypeId.m_determination == TypeIdDetermination::FailedDueToMultipleTypeIds) { - AZStd::string_view message = loadedTypeId.m_determination == TypeIdDetermination::FailedDueToMultipleTypeIds ? - "Unable to resolve provided type because the same name points to multiple types." : - "Unable to resolve provided type."; - status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown, message); - return ResolvePointerResult::FullyProcessed; + auto typeField = pointerData.FindMember(JsonSerialization::TypeIdFieldIdentifier); + if (typeField != pointerData.MemberEnd() && typeField->value.IsString()) + { + const char* format = loadedTypeId.m_determination == TypeIdDetermination::FailedToDetermine ? + "Unable to resolve provided type: %.*s." : + "Unable to resolve provided type %.*s because the same name points to multiple types."; + status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown, + AZStd::string::format(format, typeField->value.GetStringLength(), typeField->value.GetString())); + } + else + { + const char* message = loadedTypeId.m_determination == TypeIdDetermination::FailedToDetermine ? + "Unable to resolve provided type." : + "Unable to resolve provided type because the same name points to multiple types."; + status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown, message); + } + return ResolvePointerResult::FullyProcessed; } if (loadedTypeId.m_typeId != objectType) diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.h b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.h index ea236fe93c..d0a07b1a74 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.h @@ -28,6 +28,11 @@ namespace AZ FullyProcessed, ContinueProcessing }; + enum class UseTypeDeserializer : bool + { + No, + Yes + }; enum class TypeIdDetermination : u8 { ExplicitTypeId, // Type id was explicitly defined using "$type". @@ -55,10 +60,11 @@ namespace AZ JsonDeserializer(JsonDeserializer&& rhs) = delete; static JsonSerializationResult::ResultCode Load( - void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, JsonDeserializerContext& context); + void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, UseTypeDeserializer useCustom, + JsonDeserializerContext& context); static JsonSerializationResult::ResultCode LoadToPointer(void* object, const Uuid& typeId, const rapidjson::Value& value, - JsonDeserializerContext& context); + UseTypeDeserializer useCustom, JsonDeserializerContext& context); static JsonSerializationResult::ResultCode LoadWithClassElement(void* object, const rapidjson::Value& value, const SerializeContext::ClassElement& classElement, JsonDeserializerContext& context); diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.cpp index 8ade30cc6c..bc07f684f6 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.cpp @@ -245,7 +245,7 @@ namespace AZ { StackedString path(StackedString::Format::JsonPointer); JsonDeserializerContext context(settings); - result = JsonDeserializer::Load(object, objectType, root, false, context); + result = JsonDeserializer::Load(object, objectType, root, false, JsonDeserializer::UseTypeDeserializer::Yes, context); } return result; } @@ -322,7 +322,7 @@ namespace AZ JsonSerializerContext context(settings, allocator); StackedString path(StackedString::Format::ContextPath); - result = JsonSerializer::Store(output, object, defaultObject, objectType, context); + result = JsonSerializer::Store(output, object, defaultObject, objectType, JsonSerializer::UseTypeSerializer::Yes, context); } return result; } diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.cpp index a7abfca86b..f06fb36be8 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.cpp @@ -20,7 +20,7 @@ namespace AZ { JsonSerializationResult::ResultCode JsonSerializer::Store(rapidjson::Value& output, const void* object, const void* defaultObject, - const Uuid& typeId, JsonSerializerContext& context) + const Uuid& typeId, UseTypeSerializer custom, JsonSerializerContext& context) { using namespace JsonSerializationResult; @@ -32,8 +32,8 @@ namespace AZ // First check if there's a generic serializer registered for this. This makes it possible to use serializers that // are not (directly) registered with the Serialize Context. - auto serializer = context.GetRegistrationContext()->GetSerializerForType(typeId); - if (serializer) + if (BaseJsonSerializer* serializer + = (custom == UseTypeSerializer::Yes ? context.GetRegistrationContext()->GetSerializerForType(typeId) : nullptr)) { // Start by setting the object to be an explicit default. output.SetObject(); @@ -57,17 +57,18 @@ namespace AZ "No factory available to create a default object for comparison."); } void* defaultObjectPtr = AZStd::any_cast(&defaultObjectInstance); - ResultCode conversionResult = StoreWithClassData(output, object, defaultObjectPtr, *classData, StoreTypeId::No, context); + ResultCode conversionResult = StoreWithClassData(output, object, defaultObjectPtr, *classData, StoreTypeId::No + , UseTypeSerializer::Yes, context); return ResultCode::Combine(result, conversionResult); } else { - return StoreWithClassData(output, object, defaultObject, *classData, StoreTypeId::No, context); + return StoreWithClassData(output, object, defaultObject, *classData, StoreTypeId::No, custom, context); } } JsonSerializationResult::ResultCode JsonSerializer::StoreFromPointer(rapidjson::Value& output, const void* object, - const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context) + const void* defaultObject, const Uuid& typeId, UseTypeSerializer custom, JsonSerializerContext& context) { using namespace JsonSerializationResult; @@ -85,19 +86,21 @@ namespace AZ AZ_Assert(classData->m_azRtti->GetTypeId() == typeId, "Type id mismatch in '%s' during serialization to a json file. (%s vs %s)", classData->m_name, classData->m_azRtti->GetTypeId().ToString().c_str(), typeId.ToString().c_str()); - return StoreWithClassDataFromPointer(output, object, defaultObject, *classData, context); + return StoreWithClassDataFromPointer(output, object, defaultObject, *classData, custom, context); } JsonSerializationResult::ResultCode JsonSerializer::StoreWithClassData(rapidjson::Value& node, const void* object, const void* defaultObject, const SerializeContext::ClassData& classData, StoreTypeId storeTypeId, - JsonSerializerContext& context) + UseTypeSerializer custom, JsonSerializerContext& context) { using namespace JsonSerializationResult; // Start by setting the object to be an explicit default. node.SetObject(); - auto serializer = context.GetRegistrationContext()->GetSerializerForType(classData.m_typeId); + auto serializer = custom == UseTypeSerializer::Yes + ? context.GetRegistrationContext()->GetSerializerForType(classData.m_typeId) : nullptr; + if (serializer) { ResultCode result = serializer->Store(node, object, defaultObject, classData.m_typeId, context); @@ -153,7 +156,7 @@ namespace AZ } JsonSerializationResult::ResultCode JsonSerializer::StoreWithClassDataFromPointer(rapidjson::Value& output, const void* object, - const void* defaultObject, const SerializeContext::ClassData& classData, JsonSerializerContext& context) + const void* defaultObject, const SerializeContext::ClassData& classData, UseTypeSerializer custom, JsonSerializerContext& context) { using namespace JsonSerializationResult; @@ -175,7 +178,7 @@ namespace AZ } else { - return StoreWithClassData(output, object, defaultObject, *resolvedClassData, storeTypeId, context); + return StoreWithClassData(output, object, defaultObject, *resolvedClassData, storeTypeId, custom, context); } } @@ -220,8 +223,8 @@ namespace AZ { rapidjson::Value value; ResultCode result = classElement.m_flags & SerializeContext::ClassElement::FLG_POINTER ? - StoreWithClassDataFromPointer(value, object, defaultObject, *elementClassData, context): - StoreWithClassData(value, object, defaultObject, *elementClassData, StoreTypeId::No, context); + StoreWithClassDataFromPointer(value, object, defaultObject, *elementClassData, UseTypeSerializer::Yes, context): + StoreWithClassData(value, object, defaultObject, *elementClassData, StoreTypeId::No, UseTypeSerializer::Yes, context); if (result.GetProcessing() != Processing::Halted) { if (parentNode.IsObject()) diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.h b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.h index 0ccf020cee..22dd768ec5 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerializer.h @@ -26,6 +26,11 @@ namespace AZ No, Yes }; + enum class UseTypeSerializer : bool + { + No, + Yes + }; enum class ResolvePointerResult { FullyProcessed, @@ -41,16 +46,18 @@ namespace AZ JsonSerializer(JsonSerializer&& rhs) = delete; static JsonSerializationResult::ResultCode Store(rapidjson::Value& output, const void* object, const void* defaultObject, - const Uuid& typeId, JsonSerializerContext& context); + const Uuid& typeId, UseTypeSerializer useCustom, JsonSerializerContext& context); static JsonSerializationResult::ResultCode StoreFromPointer(rapidjson::Value& output, const void* object, const void* defaultObject, - const Uuid& typeId, JsonSerializerContext& context); + const Uuid& typeId, UseTypeSerializer custom, JsonSerializerContext& context); static JsonSerializationResult::ResultCode StoreWithClassData(rapidjson::Value& node, const void* object, const void* defaultObject, - const SerializeContext::ClassData& classData, StoreTypeId storeTypeId, JsonSerializerContext& context); + const SerializeContext::ClassData& classData, StoreTypeId storeTypeId, UseTypeSerializer custom, + JsonSerializerContext& context); static JsonSerializationResult::ResultCode StoreWithClassDataFromPointer(rapidjson::Value& output, const void* object, - const void* defaultObject, const SerializeContext::ClassData& classData, JsonSerializerContext& context); + const void* defaultObject, const SerializeContext::ClassData& classData, UseTypeSerializer custom, + JsonSerializerContext& context); static JsonSerializationResult::ResultCode StoreWithClassElement(rapidjson::Value& parentNode, const void* object, const void* defaultObject, const SerializeContext::ClassElement& classElement, JsonSerializerContext& context); diff --git a/Code/Framework/AtomCore/AtomCore/Serialization/Json/JsonUtils.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonUtils.cpp similarity index 98% rename from Code/Framework/AtomCore/AtomCore/Serialization/Json/JsonUtils.cpp rename to Code/Framework/AzCore/AzCore/Serialization/Json/JsonUtils.cpp index 157a6c6074..8f2ebaba8d 100644 --- a/Code/Framework/AtomCore/AtomCore/Serialization/Json/JsonUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonUtils.cpp @@ -6,7 +6,6 @@ * */ -#include #include #include #include @@ -23,6 +22,8 @@ #include #include +#include + namespace AZ { namespace JsonSerializationUtils @@ -331,6 +332,11 @@ namespace AZ // validate class name auto classData = loadSettings.m_serializeContext->FindClassData(classId); + if (!classData) + { + return AZ::Failure(AZStd::string::format("Try to load class from Id %s", classId.ToString().c_str())); + } + if (azstricmp(classData->m_name, className) != 0) { return AZ::Failure(AZStd::string::format("Try to load class %s from class %s data", classData->m_name, className)); @@ -342,9 +348,9 @@ namespace AZ { return AZ::Failure(deserializeErrors); } + return AZ::Success(); - } - + } AZ::Outcome LoadAnyObjectFromStream(IO::GenericStream& stream, const JsonDeserializerSettings* settings) { diff --git a/Code/Framework/AtomCore/AtomCore/Serialization/Json/JsonUtils.h b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonUtils.h similarity index 100% rename from Code/Framework/AtomCore/AtomCore/Serialization/Json/JsonUtils.h rename to Code/Framework/AzCore/AzCore/Serialization/Json/JsonUtils.h diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 1ea86c7b93..1a3acb2982 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -531,6 +531,8 @@ set(FILES Serialization/Json/JsonStringConversionUtils.h Serialization/Json/JsonSystemComponent.h Serialization/Json/JsonSystemComponent.cpp + Serialization/Json/JsonUtils.h + Serialization/Json/JsonUtils.cpp Serialization/Json/MapSerializer.h Serialization/Json/MapSerializer.cpp Serialization/Json/RegistrationContext.h diff --git a/Code/Framework/AtomCore/Tests/JsonSerializationUtilsTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/JsonSerializationUtilsTests.cpp similarity index 99% rename from Code/Framework/AtomCore/Tests/JsonSerializationUtilsTests.cpp rename to Code/Framework/AzCore/Tests/Serialization/Json/JsonSerializationUtilsTests.cpp index 77bcb0c08d..3444484246 100644 --- a/Code/Framework/AtomCore/Tests/JsonSerializationUtilsTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/JsonSerializationUtilsTests.cpp @@ -13,7 +13,7 @@ #include -#include +#include namespace UnitTest { diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index 911eaa7b10..63d447e9e4 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -104,6 +104,7 @@ set(FILES Serialization/Json/JsonSerializationResultTests.cpp Serialization/Json/JsonSerializationTests.h Serialization/Json/JsonSerializationTests.cpp + Serialization/Json/JsonSerializationUtilsTests.cpp Serialization/Json/JsonSerializerConformityTests.h Serialization/Json/JsonSerializerMock.h Serialization/Json/MapSerializerTests.cpp diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp index dcc14d3b91..f60759f77e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp @@ -253,6 +253,15 @@ namespace AzToolsFramework settings.m_metadata.Add(&entityIdMapper); settings.m_metadata.Create(newlyAddedEntities); + AZStd::string scratchBuffer; + auto issueReportingCallback = [&scratchBuffer]( + AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result, + AZStd::string_view path) -> AZ::JsonSerializationResult::ResultCode + { + return Internal::JsonIssueReporter(scratchBuffer, message, result, path); + }; + settings.m_reporting = AZStd::move(issueReportingCallback); + AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(instance, prefabDom, settings); AZ::Data::AssetManager::Instance().ResumeAssetRelease(); diff --git a/Code/Tools/RemoteConsole/Core/RemoteConsoleCore.cpp b/Code/Tools/RemoteConsole/Core/RemoteConsoleCore.cpp index a4e7bd24fa..d0e9e6a760 100644 --- a/Code/Tools/RemoteConsole/Core/RemoteConsoleCore.cpp +++ b/Code/Tools/RemoteConsole/Core/RemoteConsoleCore.cpp @@ -383,6 +383,13 @@ void SRemoteClient::Run() bool ok = true; bool autoCompleteDoneSent = false; + + // Send a message that is used to verify that the Remote Console connected + SNoDataEvent connectMessage; + SRemoteEventFactory::GetInst()->WriteToBuffer(&connectMessage, szBuff, size, kDefaultBufferSize); + ok &= SendPackage(szBuff, size); + ok &= RecvPackage(szBuff, size); + ok &= m_pServer->ReadBuffer(szBuff, size); while (ok) { // read data @@ -531,6 +538,7 @@ SRemoteEventFactory::SRemoteEventFactory() REGISTER_EVENT_NODATA(eCET_Strobo_FrameInfoStart); REGISTER_EVENT_STRING(eCET_Strobo_FrameInfoAdd); + REGISTER_EVENT_NODATA(eCET_ConnectMessage); } ///////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Tools/RemoteConsole/Core/RemoteConsoleCore.h b/Code/Tools/RemoteConsole/Core/RemoteConsoleCore.h index 7e22be5735..d9a6f7aba6 100644 --- a/Code/Tools/RemoteConsole/Core/RemoteConsoleCore.h +++ b/Code/Tools/RemoteConsole/Core/RemoteConsoleCore.h @@ -60,6 +60,7 @@ enum EConsoleEventType eCET_Strobo_FrameInfoStart, eCET_Strobo_FrameInfoAdd, + eCET_ConnectMessage, }; struct SRemoteEventFactory; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.cpp index 46b684493b..3493099d4c 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.cpp @@ -37,7 +37,7 @@ #include #include -#include +#include namespace ImageProcessingAtom { diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AtomShaderConfig.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/AtomShaderConfig.cpp index fcb486ec32..d2bcf4b985 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AtomShaderConfig.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AtomShaderConfig.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include namespace AZ diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.cpp index f5fb73182a..4a56c198d7 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.cpp @@ -19,7 +19,7 @@ #include -#include +#include #include #include // [GFX TODO] Remove when [ATOM-15472] diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/GlobalBuildOptions.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/GlobalBuildOptions.cpp index 66b4d2e833..da77df898b 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/GlobalBuildOptions.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/GlobalBuildOptions.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp index 9c1b3a78ca..fe1fd99f4e 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/CommonFiles/Preprocessor.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/PrecompiledShaderBuilder.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/PrecompiledShaderBuilder.cpp index 5d712a4a8a..21eda31ca2 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/PrecompiledShaderBuilder.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/PrecompiledShaderBuilder.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp index 2332f4522b..87eb6dc97d 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp index 450631eaae..fa74014479 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp index 59660440e4..0e995992b9 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderVariantAssetBuilder.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp index 4b1a6ec7f6..6fbdf5b3f0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp index db9da271bc..920326e081 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp index 8c64f1611f..d5b626917b 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Common/JsonUtils.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Common/JsonUtils.h index 549f787ac9..1a62e1753c 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Common/JsonUtils.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Common/JsonUtils.h @@ -10,9 +10,7 @@ #include #include - -#include - +#include #include #include @@ -120,6 +118,7 @@ namespace AZ AZ_Error("AZ::RPI::JsonUtils", false, "Failed to load object from json string: %s", loadResult.GetError().c_str()); return false; } + } // namespace JsonUtils } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Common/AnyAssetBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Common/AnyAssetBuilder.cpp index 4074568fbe..c9c477048f 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Common/AnyAssetBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Common/AnyAssetBuilder.cpp @@ -19,7 +19,7 @@ #include -#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp index d07f073d4f..09f6610150 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Pass/PassBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Pass/PassBuilder.cpp index f74792049f..b67f1643df 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Pass/PassBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Pass/PassBuilder.cpp @@ -11,7 +11,7 @@ #include -#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/ResourcePool/ResourcePoolBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/ResourcePool/ResourcePoolBuilder.cpp index 1dcab2679f..6cd2709d25 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/ResourcePool/ResourcePoolBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/ResourcePool/ResourcePoolBuilder.cpp @@ -16,7 +16,7 @@ #include -#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialFunctorSourceDataSerializer.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialFunctorSourceDataSerializer.cpp index 3ba115f3e6..a944a35162 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialFunctorSourceDataSerializer.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialFunctorSourceDataSerializer.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyValueSerializer.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyValueSerializer.cpp index 708644ae20..060a563c53 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyValueSerializer.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialPropertyValueSerializer.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp index 1fee57759c..9f980287cf 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceDataSerializer.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceDataSerializer.cpp index e65c65a39a..307d2bb80f 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceDataSerializer.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceDataSerializer.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialUtils.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialUtils.cpp index 67c143c074..0c2e9dca1f 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialUtils.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialUtils.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp index 29af8b7b63..319cf85fe5 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/AttachmentReadback.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp index 4cf952ee01..64ab7fdd96 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetrics.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetrics.cpp index 5df21f3964..dbb5a30756 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetrics.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetrics.cpp @@ -6,7 +6,7 @@ * */ #include -#include +#include namespace AZ { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp index c0f0e20714..8df5648432 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include diff --git a/Gems/Atom/RPI/Code/Tests.Builders/AnyAssetBuilderTest.cpp b/Gems/Atom/RPI/Code/Tests.Builders/AnyAssetBuilderTest.cpp index fcec484fa9..92e6957e9d 100644 --- a/Gems/Atom/RPI/Code/Tests.Builders/AnyAssetBuilderTest.cpp +++ b/Gems/Atom/RPI/Code/Tests.Builders/AnyAssetBuilderTest.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Tests.Builders/PassBuilderTest.cpp b/Gems/Atom/RPI/Code/Tests.Builders/PassBuilderTest.cpp index 4e088cece5..60feb7e065 100644 --- a/Gems/Atom/RPI/Code/Tests.Builders/PassBuilderTest.cpp +++ b/Gems/Atom/RPI/Code/Tests.Builders/PassBuilderTest.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include diff --git a/Gems/Atom/RPI/Code/Tests.Builders/ResourcePoolBuilderTest.cpp b/Gems/Atom/RPI/Code/Tests.Builders/ResourcePoolBuilderTest.cpp index d6107cda9e..c7e030f912 100644 --- a/Gems/Atom/RPI/Code/Tests.Builders/ResourcePoolBuilderTest.cpp +++ b/Gems/Atom/RPI/Code/Tests.Builders/ResourcePoolBuilderTest.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Tests/Common/JsonTestUtils.h b/Gems/Atom/RPI/Code/Tests/Common/JsonTestUtils.h index 2c38090b43..56d537f236 100644 --- a/Gems/Atom/RPI/Code/Tests/Common/JsonTestUtils.h +++ b/Gems/Atom/RPI/Code/Tests/Common/JsonTestUtils.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include namespace UnitTest diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialFunctorTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialFunctorTests.cpp index cf64fd12f3..0c84484508 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialFunctorTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialFunctorTests.cpp @@ -19,7 +19,6 @@ #include #include -//#include namespace UnitTest { using namespace AZ; diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp index 759842cf96..5386af1cfa 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp index 44b59059ba..5edf09b67d 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.cpp index d957ea327c..24e78b5ad3 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/Editor/AssetCollectionAsyncLoaderTestComponent.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/Editor/AssetCollectionAsyncLoaderTestComponent.cpp index b51e3b0e29..2a72641050 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/Editor/AssetCollectionAsyncLoaderTestComponent.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/Editor/AssetCollectionAsyncLoaderTestComponent.cpp @@ -13,7 +13,7 @@ #include -#include +#include // Included so we can deduce the asset type from asset paths. #include diff --git a/Gems/ExpressionEvaluation/Code/Include/ExpressionEvaluation/ExpressionEngine/ExpressionTree.h b/Gems/ExpressionEvaluation/Code/Include/ExpressionEvaluation/ExpressionEngine/ExpressionTree.h index e9da3bbfba..e47e1e84c6 100644 --- a/Gems/ExpressionEvaluation/Code/Include/ExpressionEvaluation/ExpressionEngine/ExpressionTree.h +++ b/Gems/ExpressionEvaluation/Code/Include/ExpressionEvaluation/ExpressionEngine/ExpressionTree.h @@ -11,6 +11,11 @@ #include +namespace AZ +{ + class ExpressionTreeVariableDescriptorSerializer; +} + namespace ExpressionEvaluation { // Holds all of the tokeniszed information from parsing an expression string. @@ -20,8 +25,22 @@ namespace ExpressionEvaluation { // Friend class for reflection friend class ExpressionEvaluationSystemComponent; + friend class ExpressionTreeVariableDescriptorSerializer; public: + struct VariableDescriptor + { + AZ_TYPE_INFO(VariableDescriptor, "{5E1A0044-E0E7-46D3-8BC6-A22E226ADB83}"); + + VariableDescriptor() + { + m_supportedTypes.push_back(azrtti_typeid()); + } + + AZStd::vector< AZ::Uuid > m_supportedTypes; + ExpressionVariable m_value; + }; + AZ_RTTI(ExpressionTree, "{4CCF3DFD-2EA8-47CB-AF25-353BC034EF42}"); AZ_CLASS_ALLOCATOR(ExpressionTree, AZ::SystemAllocator, 0); @@ -147,18 +166,6 @@ namespace ExpressionEvaluation private: - struct VariableDescriptor - { - AZ_TYPE_INFO(VariableDescriptor, "{5E1A0044-E0E7-46D3-8BC6-A22E226ADB83}"); - - VariableDescriptor() - { - m_supportedTypes.push_back(azrtti_typeid()); - } - - AZStd::vector< AZ::Uuid > m_supportedTypes; - ExpressionVariable m_value; - }; AZStd::unordered_map< AZ::Crc32, VariableDescriptor > m_variables; diff --git a/Gems/ExpressionEvaluation/Code/Source/ElementInformationSerializer.inl b/Gems/ExpressionEvaluation/Code/Source/ElementInformationSerializer.inl new file mode 100644 index 0000000000..8d297571ad --- /dev/null +++ b/Gems/ExpressionEvaluation/Code/Source/ElementInformationSerializer.inl @@ -0,0 +1,181 @@ +/* + * 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 +#include +#include +#include +#include +#include + +namespace AZ +{ + class ElementInformationSerializer + : public BaseJsonSerializer + { + + public: + AZ_RTTI(ElementInformationSerializer, "{B33E6AA9-C700-4E3D-857C-55F362AFE57A}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR_DECL; + + private: + using ElementInformation = ExpressionEvaluation::ElementInformation; + + static const char* EmptyAnyIdentifier; + + static bool IsEmptyAny(const rapidjson::Value& typeId) + { + if (typeId.IsString()) + { + AZStd::string_view typeName(typeId.GetString(), typeId.GetStringLength()); + return typeName == EmptyAnyIdentifier; + } + + return false; + } + + JsonSerializationResult::Result Load + ( void* outputValue + , const Uuid& outputValueTypeId + , const rapidjson::Value& inputValue + , JsonDeserializerContext& context) override + { + namespace JSR = JsonSerializationResult; + + AZ_Assert(outputValueTypeId == azrtti_typeid(), "ElementInformationSerializer Load against " + "output typeID that was not ElementInformation"); + AZ_Assert(outputValue, "ElementInformationSerializer Load against null output"); + + JsonSerializationResult::ResultCode result(JSR::Tasks::ReadField); + auto outputDatum = reinterpret_cast(outputValue); + result.Combine(ContinueLoadingFromJsonObjectField + ( &outputDatum->m_id + , azrtti_typeidm_id)>() + , inputValue + , "Id" + , context)); + + // any storage begin + { + AZ::Uuid typeId = AZ::Uuid::CreateNull(); + auto typeIdMember = inputValue.FindMember(JsonSerialization::TypeIdFieldIdentifier); + if (typeIdMember == inputValue.MemberEnd()) + { + return context.Report + ( JSR::Tasks::ReadField + , JSR::Outcomes::Missing + , AZStd::string::format("ElementInformationSerializer::Load failed to load the %s member" + , JsonSerialization::TypeIdFieldIdentifier)); + } + + if (!IsEmptyAny(typeIdMember->value)) + { + result.Combine(LoadTypeId(typeId, typeIdMember->value, context)); + if (typeId.IsNull()) + { + return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Catastrophic + , "ElementInformationSerializer::Load failed to load the AZ TypeId of the value"); + } + + AZStd::any storage = context.GetSerializeContext()->CreateAny(typeId); + if (storage.empty() || storage.type() != typeId) + { + return context.Report(result, "ElementInformationSerializer::Load failed to load a value matched the " + "reported AZ TypeId. The C++ declaration may have been deleted or changed."); + } + + result.Combine + ( ContinueLoadingFromJsonObjectField(AZStd::any_cast(&storage), typeId, inputValue, "Value", context)); + outputDatum->m_extraStore = storage; + } + } + // any storage end + + return context.Report(result, result.GetProcessing() != JSR::Processing::Halted + ? "ElementInformationSerializer Load finished loading ElementInformation" + : "ElementInformationSerializer Load failed to load ElementInformation"); + } + + JsonSerializationResult::Result Store + ( rapidjson::Value& outputValue + , const void* inputValue + , const void* defaultValue + , const Uuid& valueTypeId, JsonSerializerContext& context) override + { + namespace JSR = JsonSerializationResult; + + AZ_Assert(valueTypeId == azrtti_typeid(), "ElementInformation Store against value typeID that " + "was not ElementInformation"); + AZ_Assert(inputValue, "ElementInformation Store against null inputValue pointer "); + + auto inputScriptDataPtr = reinterpret_cast(inputValue); + auto defaultScriptDataPtr = reinterpret_cast(defaultValue); + + if (defaultScriptDataPtr) + { + if (inputScriptDataPtr->m_id == defaultScriptDataPtr->m_id + && AZ::Helpers::CompareAnyValue(inputScriptDataPtr->m_extraStore, defaultScriptDataPtr->m_extraStore)) + { + return context.Report + ( JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "ElementInformation Store used defaults for " + "ElementInformation"); + } + } + + JSR::ResultCode result(JSR::Tasks::WriteValue); + outputValue.SetObject(); + + result.Combine(ContinueStoringToJsonObjectField + ( outputValue + , "Id" + , &inputScriptDataPtr->m_id + , defaultScriptDataPtr ? &defaultScriptDataPtr->m_id : nullptr + , azrtti_typeidm_id)>() + , context)); + + if (!inputScriptDataPtr->m_extraStore.empty()) + { + rapidjson::Value typeValue; + result.Combine(StoreTypeId(typeValue, inputScriptDataPtr->m_extraStore.type(), context)); + outputValue.AddMember + ( rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier) + , AZStd::move(typeValue) + , context.GetJsonAllocator()); + + result.Combine(ContinueStoringToJsonObjectField + ( outputValue + , "Value" + , AZStd::any_cast(const_cast(&inputScriptDataPtr->m_extraStore)) + , defaultScriptDataPtr ? AZStd::any_cast(const_cast(&defaultScriptDataPtr->m_extraStore)) : nullptr + , inputScriptDataPtr->m_extraStore.type() + , context)); + + } + else + { + rapidjson::Value emptyAny; + AZStd::string emptyAnyName(EmptyAnyIdentifier); + emptyAny.SetString(emptyAnyName.c_str(), aznumeric_caster(emptyAnyName.size()), context.GetJsonAllocator()); + outputValue.AddMember + ( rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier) + , AZStd::move(emptyAny) + , context.GetJsonAllocator()); + } + + return context.Report(result, result.GetProcessing() != JSR::Processing::Halted + ? "ElementInformation Store finished saving ElementInformation" + : "ElementInformation Store failed to save ElementInformation"); + } + }; + + AZ_CLASS_ALLOCATOR_IMPL(ElementInformationSerializer, SystemAllocator, 0); + + const char* ElementInformationSerializer::EmptyAnyIdentifier = "Empty AZStd::any"; +} diff --git a/Gems/ExpressionEvaluation/Code/Source/ExpressionEvaluationSystemComponent.cpp b/Gems/ExpressionEvaluation/Code/Source/ExpressionEvaluationSystemComponent.cpp index 295d42228d..c55385595b 100644 --- a/Gems/ExpressionEvaluation/Code/Source/ExpressionEvaluationSystemComponent.cpp +++ b/Gems/ExpressionEvaluation/Code/Source/ExpressionEvaluationSystemComponent.cpp @@ -6,16 +6,17 @@ * */ -#include - #include -#include #include #include - +#include +#include #include #include #include +#include +#include +#include namespace ExpressionEvaluation { @@ -146,6 +147,12 @@ namespace ExpressionEvaluation ; } } + + if (AZ::JsonRegistrationContext* jsonContext = azrtti_cast(context)) + { + jsonContext->Serializer()->HandlesType(); + jsonContext->Serializer()->HandlesType(); + } } void ExpressionEvaluationSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) diff --git a/Gems/ExpressionEvaluation/Code/Source/ExpressionPrimitivesSerializers.inl b/Gems/ExpressionEvaluation/Code/Source/ExpressionPrimitivesSerializers.inl new file mode 100644 index 0000000000..76f43a9b82 --- /dev/null +++ b/Gems/ExpressionEvaluation/Code/Source/ExpressionPrimitivesSerializers.inl @@ -0,0 +1,147 @@ +/* + * 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 +#include +#include +#include +#include +#include + +namespace AZ +{ + class ExpressionTreeVariableDescriptorSerializer + : public BaseJsonSerializer + { + + public: + AZ_RTTI(ExpressionTreeVariableDescriptorSerializer, "{5EFF37D6-BD54-45C6-9FC6-B1E0D3A8204C}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR_DECL; + + private: + using VariableDescriptor = ExpressionEvaluation::ExpressionTree::VariableDescriptor; + + JsonSerializationResult::Result Load + ( void* outputValue + , const Uuid& outputValueTypeId + , const rapidjson::Value& inputValue + , JsonDeserializerContext& context) override + { + namespace JSR = JsonSerializationResult; + + AZ_Assert(outputValueTypeId == azrtti_typeid(), "ExpressionTreeVariableDescriptorSerializer Load against " + "output typeID that was not VariableDescriptor"); + AZ_Assert(outputValue, "ExpressionTreeVariableDescriptorSerializer Load against null output"); + + JsonSerializationResult::ResultCode result(JSR::Tasks::ReadField); + auto outputDatum = reinterpret_cast(outputValue); + + result.Combine(ContinueLoadingFromJsonObjectField + ( &outputDatum->m_supportedTypes + , azrtti_typeidm_supportedTypes)>() + , inputValue + , "SupportedTypes" + , context)); + + // any storage begin + AZ::Uuid typeId = AZ::Uuid::CreateNull(); + auto typeIdMember = inputValue.FindMember(JsonSerialization::TypeIdFieldIdentifier); + if (typeIdMember == inputValue.MemberEnd()) + { + return context.Report + ( JSR::Tasks::ReadField + , JSR::Outcomes::Missing + , AZStd::string::format("ExpressionTreeVariableDescriptorSerializer::Load failed to load the %s member" + , JsonSerialization::TypeIdFieldIdentifier)); + } + + result.Combine(LoadTypeId(typeId, typeIdMember->value, context)); + if (typeId.IsNull()) + { + return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Catastrophic + , "ExpressionTreeVariableDescriptorSerializer::Load failed to load the AZ TypeId of the value"); + } + + AZStd::any storage = context.GetSerializeContext()->CreateAny(typeId); + if (storage.empty() || storage.type() != typeId) + { + return context.Report(result, "ExpressionTreeVariableDescriptorSerializer::Load failed to load a value matched the " + "reported AZ TypeId. The C++ declaration may have been deleted or changed."); + } + + result.Combine(ContinueLoadingFromJsonObjectField(AZStd::any_cast(&storage), typeId, inputValue, "Value", context)); + outputDatum->m_value = storage; + // any storage end + + return context.Report(result, result.GetProcessing() != JSR::Processing::Halted + ? "ExpressionTreeVariableDescriptorSerializer Load finished loading VariableDescriptor" + : "ExpressionTreeVariableDescriptorSerializer Load failed to load VariableDescriptor"); + } + + JsonSerializationResult::Result Store + ( rapidjson::Value& outputValue + , const void* inputValue + , const void* defaultValue + , const Uuid& valueTypeId, JsonSerializerContext& context) override + { + namespace JSR = JsonSerializationResult; + + AZ_Assert(valueTypeId == azrtti_typeid(), "VariableDescriptor Store against value typeID that " + "was not VariableDescriptor"); + AZ_Assert(inputValue, "VariableDescriptor Store against null inputValue pointer "); + + auto inputScriptDataPtr = reinterpret_cast(inputValue); + auto defaultScriptDataPtr = reinterpret_cast(defaultValue); + + if (defaultScriptDataPtr) + { + if (inputScriptDataPtr->m_supportedTypes == defaultScriptDataPtr->m_supportedTypes + && AZ::Helpers::CompareAnyValue(inputScriptDataPtr->m_value, defaultScriptDataPtr->m_value)) + { + return context.Report + ( JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "VariableDescriptor Store used defaults for " + "VariableDescriptor"); + } + } + + JSR::ResultCode result(JSR::Tasks::WriteValue); + outputValue.SetObject(); + + result.Combine(ContinueStoringToJsonObjectField + ( outputValue + , "SupportedTypes" + , &inputScriptDataPtr->m_supportedTypes + , defaultScriptDataPtr ? &defaultScriptDataPtr->m_supportedTypes : nullptr + , azrtti_typeidm_supportedTypes)>() + , context)); + + rapidjson::Value typeValue; + result.Combine(StoreTypeId(typeValue, inputScriptDataPtr->m_value.type(), context)); + outputValue.AddMember + ( rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier) + , AZStd::move(typeValue) + , context.GetJsonAllocator()); + + result.Combine(ContinueStoringToJsonObjectField + ( outputValue + , "Value" + , AZStd::any_cast(const_cast(&inputScriptDataPtr->m_value)) + , defaultScriptDataPtr ? AZStd::any_cast(const_cast(&defaultScriptDataPtr->m_value)) : nullptr + , inputScriptDataPtr->m_value.type() + , context)); + + return context.Report(result, result.GetProcessing() != JSR::Processing::Halted + ? "VariableDescriptor Store finished saving VariableDescriptor" + : "VariableDescriptor Store failed to save VariableDescriptor"); + } + }; + + AZ_CLASS_ALLOCATOR_IMPL(ExpressionTreeVariableDescriptorSerializer, SystemAllocator, 0); +} diff --git a/Gems/ExpressionEvaluation/Code/expressionevaluation_files.cmake b/Gems/ExpressionEvaluation/Code/expressionevaluation_files.cmake index 00d546459a..908622c163 100644 --- a/Gems/ExpressionEvaluation/Code/expressionevaluation_files.cmake +++ b/Gems/ExpressionEvaluation/Code/expressionevaluation_files.cmake @@ -13,6 +13,8 @@ set(FILES Include/ExpressionEvaluation/ExpressionEngine/ExpressionTypes.h Source/ExpressionEvaluationSystemComponent.cpp Source/ExpressionEvaluationSystemComponent.h + Source/ExpressionPrimitivesSerializers.inl + Source/ElementInformationSerializer.inl Source/ExpressionEngine/ExpressionElementParser.h Source/ExpressionEngine/ExpressionPrimitive.cpp Source/ExpressionEngine/ExpressionPrimitive.h diff --git a/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAsset.cpp b/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAsset.cpp index adada5791e..18736db12c 100644 --- a/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAsset.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAsset.cpp @@ -114,11 +114,13 @@ namespace ScriptCanvasEditor ScriptCanvas::ScriptCanvasData& ScriptCanvasAsset::GetScriptCanvasData() { + AZ_Assert(m_data != nullptr, "ScriptCanvasData not initialized, it must be created on construction"); return *m_data; } const ScriptCanvas::ScriptCanvasData& ScriptCanvasAsset::GetScriptCanvasData() const { + AZ_Assert(m_data != nullptr, "ScriptCanvasData not initialized, it must be created on construction"); return *m_data; } } diff --git a/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAssetHandler.cpp b/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAssetHandler.cpp index 55da572939..6f86b93dda 100644 --- a/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAssetHandler.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAssetHandler.cpp @@ -6,29 +6,47 @@ * */ - -#include -#include - +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include #include -#include #include -#include #include +#include +#include -#include - -#include -#include -#include -#include -#include -#include -#include +namespace ScriptCanvasAssetHandlerCpp +{ + using namespace ScriptCanvas; -#include -#include + void CollectNodes(const GraphData::NodeContainer& container, SerializationListeners& listeners) + { + for (auto& nodeEntity : container) + { + if (nodeEntity) + { + if (auto listener = azrtti_cast(AZ::EntityUtils::FindFirstDerivedComponent(nodeEntity))) + { + listeners.push_back(listener); + } + } + } + } +} namespace ScriptCanvasEditor { @@ -75,26 +93,84 @@ namespace ScriptCanvasEditor } } - - AZ::Data::AssetHandler::LoadResult ScriptCanvasAssetHandler::LoadAssetData( - const AZ::Data::Asset& asset, - AZStd::shared_ptr stream, - const AZ::Data::AssetFilterCB& assetLoadFilterCB) + AZ::Data::AssetHandler::LoadResult ScriptCanvasAssetHandler::LoadAssetData + ( const AZ::Data::Asset& assetTarget + , AZStd::shared_ptr streamSource + , [[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) { - auto* scriptCanvasAsset = asset.GetAs(); - AZ_Assert(scriptCanvasAsset, "This should be an scene slice asset, as this is the only type we process!"); - if (scriptCanvasAsset && m_serializeContext) + namespace JSRU = AZ::JsonSerializationUtils; + using namespace ScriptCanvas; + + auto* scriptCanvasAssetTarget = assetTarget.GetAs(); + AZ_Assert(scriptCanvasAssetTarget, "This should be a ScriptCanvasAsset, as this is the only type we process!"); + + if (m_serializeContext + && streamSource + && scriptCanvasAssetTarget) { - stream->Seek(0U, AZ::IO::GenericStream::ST_SEEK_BEGIN); - // tolerate unknown classes in the editor. Let the asset processor warn about bad nodes... - bool loadSuccess = AZ::Utils::LoadObjectFromStreamInPlace(*stream, scriptCanvasAsset->GetScriptCanvasData(), m_serializeContext, AZ::ObjectStream::FilterDescriptor(assetLoadFilterCB, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES)); - return loadSuccess ? AZ::Data::AssetHandler::LoadResult::LoadComplete : AZ::Data::AssetHandler::LoadResult::Error; + streamSource->Seek(0U, AZ::IO::GenericStream::ST_SEEK_BEGIN); + auto& scriptCanvasDataTarget = scriptCanvasAssetTarget->GetScriptCanvasData(); + AZStd::vector byteBuffer; + byteBuffer.resize_no_construct(streamSource->GetLength()); + AZ::IO::ByteContainerStream byteStreamSource(&byteBuffer); + const size_t bytesRead = streamSource->Read(byteBuffer.size(), byteBuffer.data()); + scriptCanvasDataTarget.m_scriptCanvasEntity.reset(nullptr); + + if (bytesRead == streamSource->GetLength()) + { + byteStreamSource.Seek(0U, AZ::IO::GenericStream::ST_SEEK_BEGIN); + AZ::JsonDeserializerSettings settings; + settings.m_serializeContext = m_serializeContext; + settings.m_metadata.Create(); + // attempt JSON deserialization... + if (JSRU::LoadObjectFromStreamByType + ( &scriptCanvasDataTarget + , azrtti_typeid() + , byteStreamSource + , &settings).IsSuccess()) + { + if (auto graphData = scriptCanvasAssetTarget->GetScriptCanvasGraph() + ? scriptCanvasAssetTarget->GetScriptCanvasGraph()->GetGraphData() + : nullptr) + { + auto listeners = settings.m_metadata.Find(); + AZ_Assert(listeners, "Failed to create SerializationListeners"); + + ScriptCanvasAssetHandlerCpp::CollectNodes(graphData->m_nodes, *listeners); + + for (auto listener : *listeners) + { + listener->OnDeserialize(); + } + + return AZ::Data::AssetHandler::LoadResult::LoadComplete; + } + else + { + AZ_Warning("ScriptCanvas", false, "ScriptCanvasAssetHandler::LoadAssetData failed to load graph data from JOSON"); + } + } +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// + else + {// ...if there is a failure, check if it is saved in the old format + byteStreamSource.Seek(0U, AZ::IO::GenericStream::ST_SEEK_BEGIN); + // tolerate unknown classes in the editor. Let the asset processor warn about bad nodes... + if (AZ::Utils::LoadObjectFromStreamInPlace + (byteStreamSource + , scriptCanvasDataTarget + , m_serializeContext + , AZ::ObjectStream::FilterDescriptor(assetLoadFilterCB, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES))) + { + return AZ::Data::AssetHandler::LoadResult::LoadComplete; + } + } +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) + } } - return AZ::Data::AssetHandler::LoadResult::Error; + return AZ::Data::AssetHandler::LoadResult::Error; } - bool ScriptCanvasAssetHandler::SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) { return SaveAssetData(asset.GetAs(), stream); @@ -105,20 +181,42 @@ namespace ScriptCanvasEditor return SaveAssetData(assetData, stream, AZ::DataStream::ST_XML); } - bool ScriptCanvasAssetHandler::SaveAssetData(const ScriptCanvasAsset* assetData, AZ::IO::GenericStream* stream, AZ::DataStream::StreamType streamType) + bool ScriptCanvasAssetHandler::SaveAssetData + ( const ScriptCanvasAsset* assetData + , AZ::IO::GenericStream* stream + , [[maybe_unused]] AZ::DataStream::StreamType streamType) { - if (assetData && m_serializeContext) + namespace JSRU = AZ::JsonSerializationUtils; + using namespace ScriptCanvas; + + if (m_serializeContext + && stream + && assetData + && assetData->GetScriptCanvasGraph() + && assetData->GetScriptCanvasGraph()->GetGraphData()) { - AZStd::vector byteBuffer; - AZ::IO::ByteContainerStream byteStream(&byteBuffer); - AZ::ObjectStream* objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, streamType); - bool scriptCanvasAssetSaved = objStream->WriteClass(&assetData->GetScriptCanvasData()); - objStream->Finalize(); - scriptCanvasAssetSaved = stream->Write(byteBuffer.size(), byteBuffer.data()) == byteBuffer.size() && scriptCanvasAssetSaved; - return scriptCanvasAssetSaved; - } + auto graphData = assetData->GetScriptCanvasGraph()->GetGraphData(); + AZ::JsonSerializerSettings settings; + settings.m_metadata.Create(); + auto listeners = settings.m_metadata.Find(); + AZ_Assert(listeners, "Failed to create SerializationListeners"); + ScriptCanvasAssetHandlerCpp::CollectNodes(graphData->m_nodes, *listeners); + settings.m_keepDefaults = false; + settings.m_serializeContext = m_serializeContext; + + for (auto listener : *listeners) + { + listener->OnSerialize(); + } - return false; + return JSRU::SaveObjectToStream(&assetData->GetScriptCanvasData(), *stream, nullptr, &settings).IsSuccess(); + } + else + { + AZ_Error("ScriptCanvas", false, "Saving ScriptCavas assets in the handler requires a valid IO stream, " + "asset pointer, and serialize context"); + return false; + } } void ScriptCanvasAssetHandler::DestroyAsset(AZ::Data::AssetPtr ptr) @@ -126,17 +224,11 @@ namespace ScriptCanvasEditor delete ptr; } - //========================================================================= - // GetSerializeContext - //=========================================================================. AZ::SerializeContext* ScriptCanvasAssetHandler::GetSerializeContext() const { return m_serializeContext; } - //========================================================================= - // SetSerializeContext - //=========================================================================. void ScriptCanvasAssetHandler::SetSerializeContext(AZ::SerializeContext* context) { m_serializeContext = context; @@ -147,22 +239,17 @@ namespace ScriptCanvasEditor EBUS_EVENT_RESULT(m_serializeContext, AZ::ComponentApplicationBus, GetSerializeContext); if (!m_serializeContext) { - AZ_Error("Script Canvas", false, "ScriptCanvasAssetHandler: No serialize context provided! We will not be able to process Graph Asset type"); + AZ_Error("Script Canvas", false, "ScriptCanvasAssetHandler: No serialize context provided! " + "We will not be able to process Graph Asset type"); } } } - //========================================================================= - // GetHandledAssetTypes - //=========================================================================. void ScriptCanvasAssetHandler::GetHandledAssetTypes(AZStd::vector& assetTypes) { assetTypes.push_back(GetAssetType()); } - //========================================================================= - // GetAssetType - //=========================================================================. AZ::Data::AssetType ScriptCanvasAssetHandler::GetAssetType() const { return ScriptCanvasAssetHandler::GetAssetTypeStatic(); @@ -178,38 +265,24 @@ namespace ScriptCanvasEditor return azrtti_typeid(); } - //========================================================================= - // GetAssetTypeExtensions - //=========================================================================. void ScriptCanvasAssetHandler::GetAssetTypeExtensions(AZStd::vector& extensions) { ScriptCanvasAsset::Description description; extensions.push_back(description.GetExtensionImpl()); } - //========================================================================= - // GetComponentTypeId - //=========================================================================. AZ::Uuid ScriptCanvasAssetHandler::GetComponentTypeId() const { return azrtti_typeid(); } - //========================================================================= - // GetGroup - //=========================================================================. const char* ScriptCanvasAssetHandler::GetGroup() const { return ScriptCanvas::AssetDescription::GetGroup(); } - //========================================================================= - // GetBrowserIcon - //=========================================================================. const char* ScriptCanvasAssetHandler::GetBrowserIcon() const { return ScriptCanvas::AssetDescription::GetIconPath(); } - - } diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ValidationPanel/GraphValidationDockWidget.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ValidationPanel/GraphValidationDockWidget.cpp index da7fab5405..ded0130001 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ValidationPanel/GraphValidationDockWidget.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ValidationPanel/GraphValidationDockWidget.cpp @@ -734,7 +734,7 @@ namespace ScriptCanvasEditor { ui->statusTableView->clearSelection(); - if (auto model = GetActiveData().second->GetModel()) + if (auto model = GetActiveData().second ? GetActiveData().second->GetModel() : nullptr) { model->Clear(); model->RunValidation(m_activeGraphIds.scriptCanvasId); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Header.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Header.jinja index d743a95ee1..332bbfa7da 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Header.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Header.jinja @@ -43,10 +43,8 @@ SPDX-License-Identifier: Apache-2.0 OR MIT namespace {{attribute_Namespace}} { {% endif %} - {% set className = Class.attrib['Name'] %} -{% set baseClass = Class.attrib['Base'] %} - +{% set baseClasses = ", ".join(Class.attrib['Base'].split(';')) if Class.attrib['Base'] is string else "ScriptCanvas::Node" %} ///////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////// //// @@ -66,7 +64,7 @@ namespace {{attribute_Namespace}} // You must #include the generated header into the source header #define SCRIPTCANVAS_NODE_{{ className }} \ public: \ - AZ_COMPONENT({{ className }}, "{{ classUuid }}"{% if baseClass is defined %}, {{ baseClass }}{% endif %}); \ + AZ_COMPONENT({{ className }}, "{{ classUuid }}", {{ baseClasses }} ); \ static void Reflect(AZ::ReflectContext* reflection); \ void ConfigureSlots() override; \ bool RequiresDynamicSlotOrdering() const override; \ diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Source.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Source.jinja index 6a381398c1..64e92c1ac7 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Source.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Source.jinja @@ -4,6 +4,8 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT #} +{% import 'ScriptCanvas_Macros.jinja' as macros %} + {% macro add_attribute(attribute, tags) %} {% set value = tags[attribute] %} {% if value is defined %} @@ -26,7 +28,7 @@ SPDX-License-Identifier: Apache-2.0 OR MIT #include "{{ xml.attrib['Include'] }}" {% for Class in xml.iter('Class') %} - +{% set baseClass = Class.attrib['Base'].split(';')[0] if Class.attrib['Base'] is string else "ScriptCanvas::Node" %} {% set attribute_Namespace = undefined %} {%- if Class.attrib['Namespace'] is defined %} {% if Class.attrib['Namespace'] != "None" %} @@ -42,10 +44,8 @@ namespace {{attribute_Namespace}} void {{ Class.attrib['QualifiedName'] }}::ConfigureSlots() { -{% if Class.attrib['Base'] is defined %} - {{ Class.attrib['Base'] }}::ConfigureSlots(); + {{ baseClass }}::ConfigureSlots(); -{% endif %} {% for Property in Class.iter('Property') %} {% if Property.attrib['IsInput'] | booleanTrue %} // {{ Property.attrib['Name'] }} @@ -208,15 +208,15 @@ void {{ Class.attrib['QualifiedName'] }}::Reflect(AZ::ReflectContext* context) {% endfor %} {% endif %} -{% if Class.attrib['Base'] is defined %} - static_assert((std::is_base_of::value), "Script Canvas nodes require the first base class to be derived from ScriptCanvas::Node"); + static_assert((std::is_base_of::value), "Script Canvas nodes require the first base class to be derived from ScriptCanvas::Node"); -{% endif %} if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) { - serializeContext->Class<{{ Class.attrib['QualifiedName'] }}{% if Class.attrib['Base'] is defined %}, {{ Class.attrib['Base'] }}{% endif %}>() + serializeContext->Class<{{ Class.attrib['QualifiedName'] }}, {{ baseClass }}>() {% if Class.attrib['EventHandler'] is defined %} +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// ->EventHandler<{{ Class.attrib['EventHandler'] }}>() +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) {% endif %} {% if Class.attrib['Version'] is defined %} ->Version({{ Class.attrib['Version'] }}{% if Class.attrib['VersionConverter'] is defined %}, &{{ Class.attrib['VersionConverter'] }}{% endif %}) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja index 8bb72bb37f..236866ffeb 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja @@ -160,7 +160,9 @@ void {{attribute_QualifiedName}}::Reflect(AZ::ReflectContext* context) { {% if ExtendReflectionSerialize is defined %} auto {{preSerialize}} = {% else %} {% endif %}serializeContext->Class<{{ attribute_Name }}{% if attribute_Base is defined %}, {{ attribute_Base }}{% endif %}>(){{postSerialize}} {% if attribute_EventHandler is defined %} +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// {{preSerialize}}->EventHandler<{{ attribute_EventHandler }}>(){{postSerialize}} +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) {% endif %} {# Serialized Properties #} diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvas_Macros.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvas_Macros.jinja index 508bf65454..0b9242bf3d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvas_Macros.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvas_Macros.jinja @@ -173,11 +173,10 @@ AZStd::tuple<{{returnTypes|join(", ")}}> {# ------- #} - {# ------------------------------------------------------------------------------------- #} {# NODEABLES #} -{# TODO-LS: This macro lacks parsing the parameters provided to a Contract tag #} +{# TODO: This macro lacks parsing the parameters provided to a Contract tag #} {%- macro AddContract(configurationName, item) -%} {% set contracts = [] %} diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h index 5e4d11ac65..76542d6097 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h @@ -24,6 +24,8 @@ #include #include +#define OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED + namespace AZ { class Entity; @@ -94,7 +96,7 @@ namespace ScriptCanvas struct VersionData { - AZ_TYPE_INFO(VersionData, "{14C629F6-467B-46FE-8B63-48FDFCA42175}"); + AZ_TYPE_INFO(VersionData, "{52036892-DA63-4199-AC6A-9BAFE6B74EFC}"); static void Reflect(AZ::ReflectContext* context); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp index 4dae63d71b..e2e0ba1c9d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp @@ -2041,7 +2041,7 @@ namespace ScriptCanvas } } - void Datum::OnWriteEnd() + void Datum::OnDeserialize() { if (m_type.GetType() == Data::eType::BehaviorContextObject) { @@ -2059,18 +2059,27 @@ namespace ScriptCanvas } else { - AZ_Error("Script Canvas", false, AZStd::string::format("Datum type (%s) de-serialized, but no such class found in the behavior context", m_type.GetAZType().ToString().c_str()).c_str()); + AZ_Error("ScriptCanvas", false, AZStd::string::format("Datum type (%s) de-serialized, but no such class found in the behavior context", m_type.GetAZType().ToString().c_str()).c_str()); } } } +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// + void Datum::OnWriteEnd() + { + OnDeserialize(); + } +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) + void Datum::Reflect(AZ::ReflectContext* reflection) { if (auto serializeContext = azrtti_cast(reflection)) { serializeContext->Class() ->Version(DatumHelpers::Version::Current, &DatumHelpers::VersionConverter) +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// ->EventHandler() +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) ->Field("m_isUntypedStorage", &Datum::m_isOverloadedStorage) ->Field("m_type", &Datum::m_type) ->Field("m_originality", &Datum::m_originality) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.h index 4cbc25b500..2566149b6f 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.h @@ -8,14 +8,15 @@ #pragma once +#include #include #include #include #include +#include +#include #include #include -#include -#include namespace AZ { @@ -33,11 +34,12 @@ namespace ScriptCanvas /// A Datum is used to provide generic storage for all data types in ScriptCanvas, and provide a common interface to accessing, modifying, and displaying them /// in the editor, regardless of their actual ScriptCanvas or BehaviorContext type. class Datum final + : public SerializationListener { friend class AZ::DatumSerializer; public: - AZ_TYPE_INFO(Datum, "{8B836FC0-98A8-4A81-8651-35C7CA125451}"); + AZ_RTTI(Datum, "{8B836FC0-98A8-4A81-8651-35C7CA125451}", SerializationListener); AZ_CLASS_ALLOCATOR(Datum, AZ::SystemAllocator, 0); enum class eOriginality : int @@ -234,6 +236,7 @@ namespace ScriptCanvas } }; +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// class SerializeContextEventHandler : public AZ::SerializeContext::IEventHandler { public: @@ -244,6 +247,7 @@ namespace ScriptCanvas datum->OnWriteEnd(); } }; +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) friend class SerializeContextEventHandler; @@ -341,9 +345,11 @@ namespace ScriptCanvas void OnDatumEdited(); - void OnReadBegin(); - + void OnDeserialize() override; + +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// void OnWriteEnd(); +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) AZ_INLINE bool SatisfiesTraits(AZ::u8 behaviorValueTraits) const; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/GraphData.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/GraphData.cpp index 9bed478d74..77de1330e2 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/GraphData.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/GraphData.cpp @@ -19,17 +19,17 @@ namespace AZ namespace ScriptCanvas { +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// class GraphDataEventHandler : public AZ::SerializeContext::IEventHandler { public: /// Called to rebuild the Endpoint map void OnWriteEnd(void* classPtr) override { - auto* graphData = reinterpret_cast(classPtr); - graphData->BuildEndpointMap(); - graphData->LoadDependentAssets(); + reinterpret_cast(classPtr)->OnDeserialized(); } }; +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) void GraphData::Reflect(AZ::ReflectContext* context) { @@ -42,7 +42,9 @@ namespace ScriptCanvas serializeContext->Class() ->Version(4, &GraphData::VersionConverter) +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// ->EventHandler() +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) ->Field("m_nodes", &GraphData::m_nodes) ->Field("m_connections", &GraphData::m_connections) ->Field("m_dependentAssets", &GraphData::m_dependentAssets) @@ -215,4 +217,10 @@ namespace ScriptCanvas m_dependentAssets.clear(); } + + void GraphData::OnDeserialized() + { + BuildEndpointMap(); + LoadDependentAssets(); + } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/GraphData.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/GraphData.h index f09098ca17..cdd23bbdda 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/GraphData.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/GraphData.h @@ -37,6 +37,7 @@ namespace ScriptCanvas void BuildEndpointMap(); void Clear(bool deleteData = false); void LoadDependentAssets(); + void OnDeserialized(); using NodeContainer = AZStd::unordered_set; using ConnectionContainer = AZStd::vector; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.cpp index 7dda4d4609..933531f5be 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.cpp @@ -60,6 +60,7 @@ namespace ScriptCanvas // Node ///////// +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// class NodeEventHandler : public AZ::SerializeContext::IEventHandler { @@ -67,9 +68,10 @@ namespace ScriptCanvas void OnWriteEnd(void* objectPtr) override { auto node = reinterpret_cast(objectPtr); - node->RebuildInternalState(); + node->OnDeserialize(); } }; +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) bool NodeVersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& nodeElementNode) { @@ -420,7 +422,9 @@ namespace ScriptCanvas serializeContext->RegisterGenericType>(); serializeContext->Class() +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// ->EventHandler() +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) ->Version(NodeCpp::Version::Current, &NodeVersionConverter) ->Field("Slots", &Node::m_slots) ->Field("Datums", &Node::m_slotDatums) @@ -2614,6 +2618,11 @@ namespace ScriptCanvas } } + void Node::OnDeserialize() + { + RebuildInternalState(); + } + void Node::OnEndpointConnected(const Endpoint& endpoint) { const SlotId& currentSlotId = EndpointNotificationBus::GetCurrentBusId()->GetSlotId(); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h index bf3e17156b..27f0b3862d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h @@ -14,25 +14,23 @@ #include #include #include - +#include #include #include #include #include -#include #include #include #include +#include #include +#include #include +#include #include #include -#include #include -#include - -#include - +#include #define SCRIPT_CANVAS_CALL_ON_INDEX_SEQUENCE(lambdaInterior)\ int dummy[]{ 0, ( lambdaInterior , 0)... };\ @@ -65,6 +63,7 @@ namespace ScriptCanvas struct BehaviorContextMethodHelper; +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// template class SerializeContextReadWriteHandler : public AZ::SerializeContext::IEventHandler { @@ -128,6 +127,7 @@ namespace ScriptCanvas deserializedObject->OnWriteEnd(); } }; +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) // List of slots that will be create visual only slots on the nodes. // Useful for special configurations or editor only concepts. @@ -401,6 +401,7 @@ namespace ScriptCanvas , public DatumNotificationBus::Handler , public NodeRequestBus::Handler , public EndpointNotificationBus::MultiHandler + , public SerializationListener { friend class Graph; friend class RuntimeComponent; @@ -472,7 +473,7 @@ namespace ScriptCanvas public: - AZ_COMPONENT(Node, "{52B454AE-FA7E-4FE9-87D3-A1CAB235C691}"); + AZ_COMPONENT(Node, "{52B454AE-FA7E-4FE9-87D3-A1CAB235C691}", SerializationListener); static void Reflect(AZ::ReflectContext* reflection); Node(); @@ -821,6 +822,7 @@ namespace ScriptCanvas ////////////////////////////////////////////////////////////////////////// protected: + void OnDeserialize() override; virtual void OnReconfigurationBegin() {} virtual void OnReconfigurationEnd() {} diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/SerializationListener.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/SerializationListener.h new file mode 100644 index 0000000000..4a3771221d --- /dev/null +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/SerializationListener.h @@ -0,0 +1,25 @@ +/* + * 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 + +namespace ScriptCanvas +{ + class SerializationListener + { + public: + AZ_RTTI(SerializationListener, "{CA4EE281-30B3-4928-BCD8-9305CE75E463}"); + virtual ~SerializationListener() {} + + virtual void OnSerialize() {} + + virtual void OnDeserialize() {} + }; + + using SerializationListeners = AZStd::vector; +} diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Data/BehaviorContextObject.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Data/BehaviorContextObject.cpp index 09d356a2f0..7bcf0b2282 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Data/BehaviorContextObject.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Data/BehaviorContextObject.cpp @@ -17,26 +17,12 @@ namespace ScriptCanvas { - void BehaviorContextObject::OnReadBegin() - { - if (!IsOwned()) - { - Clear(); - } - } - - void BehaviorContextObject::OnWriteEnd() - { - // Id Remapping invokes this method as well, not just serializing from an ObjectStream - } - void BehaviorContextObject::Reflect(AZ::ReflectContext* reflection) { if (auto serializeContext = azrtti_cast(reflection)) { serializeContext->Class() ->Version(0) - ->EventHandler() ->Field("m_flags", &BehaviorContextObject::m_flags) ->Field("m_object", &BehaviorContextObject::m_object) ; @@ -55,24 +41,11 @@ namespace ScriptCanvas } } - void BehaviorContextObject::SerializeContextEventHandler::OnReadBegin(void* classPtr) - { - BehaviorContextObject* object = reinterpret_cast(classPtr); - object->OnReadBegin(); - } - - void BehaviorContextObject::SerializeContextEventHandler::OnWriteEnd(void* classPtr) - { - BehaviorContextObject* object = reinterpret_cast(classPtr); - object->OnWriteEnd(); - } - BehaviorContextObjectPtr BehaviorContextObject::CloneObject(const AZ::BehaviorClass& behaviorClass) { if (SystemRequestBus::HasHandlers()) { AZStd::vector buffer; - { bool wasOwned = IsOwned(); m_flags |= Flags::Owned; @@ -87,7 +60,6 @@ namespace ScriptCanvas } AZ::IO::ByteContainerStream> readStream(&buffer); - BehaviorContextObject* newObject = CreateDefault(behaviorClass); AZ::Utils::LoadObjectFromStreamInPlace(readStream, (*newObject)); @@ -124,6 +96,12 @@ namespace ScriptCanvas : BehaviorContextObjectPtr(aznew BehaviorContextObject(reference, GetAnyTypeInfoReference(typeID), referenceFlags)); } + void BehaviorContextObject::Deserialize(BehaviorContextObject& target, const AZ::BehaviorClass& behaviorClass, AZStd::any& source) + { + target.m_object = AZStd::move(AZStd::any(AZStd::any_cast(&source), GetAnyTypeInfoObject(behaviorClass))); + target.m_flags = Owned; + } + void BehaviorContextObject::release() { if (--m_referenceCount == 0) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Data/BehaviorContextObject.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Data/BehaviorContextObject.h index 47e2dc472b..d7fc7dcc91 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Data/BehaviorContextObject.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Data/BehaviorContextObject.h @@ -22,6 +22,7 @@ namespace AZ { class ReflectContext; + class BehaviorContextObjectSerializer; } namespace ScriptCanvas @@ -29,6 +30,8 @@ namespace ScriptCanvas class BehaviorContextObject final { friend struct AZStd::IntrusivePtrCountPolicy; + friend class Datum; + friend class AZ::BehaviorContextObjectSerializer; public: AZ_TYPE_INFO(BehaviorContextObject, "{B735214D-5182-4536-B748-61EC83C1F007}"); @@ -69,16 +72,6 @@ namespace ScriptCanvas Reference = 1 << 3, }; - class SerializeContextEventHandler : public AZ::SerializeContext::IEventHandler - { - public: - /// Called right before we start reading from the instance pointed by classPtr. - void OnReadBegin(void* classPtr) override; - - /// Called after we are done writing to the instance pointed by classPtr. - void OnWriteEnd(void* classPtr) override; - }; - template static AZ::BehaviorObject InvokeConstructor(const AZ::BehaviorClass& behaviorClass, void* resultPtr, Args&&... args); @@ -90,6 +83,8 @@ namespace ScriptCanvas AZ_INLINE static BehaviorContextObject* CreateDefaultHeap(const AZ::BehaviorClass& behaviorClass); + static void Deserialize(BehaviorContextObject& target, const AZ::BehaviorClass& behaviorClass, AZStd::any& source); + // use the SSO optimization on behavior class size ALIGNED with a placement new of behavior class create AZ_FORCE_INLINE static AnyTypeInfo GetAnyTypeInfoObject(const AZ::BehaviorClass& behaviorClass); @@ -110,6 +105,7 @@ namespace ScriptCanvas // it is very important to track these from the moment they are created... friend struct AZ::Serialize::InstanceFactory; friend struct AZ::AnyTypeInfoConcept; + //...so don't use the ctors, use the Create functions... //...the friend declarations are here for compatibility with the serialization system only AZ_FORCE_INLINE BehaviorContextObject() = default; @@ -128,10 +124,6 @@ namespace ScriptCanvas AZ_FORCE_INLINE bool IsOwned() const; - void OnReadBegin(); - - void OnWriteEnd(); - AZ_FORCE_INLINE void add_ref(); void release(); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/EBusEventHandler.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/EBusEventHandler.cpp index af583738da..ef73bb967d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/EBusEventHandler.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/EBusEventHandler.cpp @@ -602,14 +602,23 @@ namespace ScriptCanvas } } - void EBusEventHandler::OnWriteEnd() + void EBusEventHandler::OnDeserialize() { AZStd::lock_guard lock(m_mutex); if (!m_ebus) { CreateHandler(m_ebusName); } + + Node::OnDeserialize(); + } + +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// + void EBusEventHandler::OnWriteEnd() + { + OnDeserialize(); } +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) NodeTypeIdentifier EBusEventHandler::GetOutputNodeType(const SlotId& slotId) const { diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/EBusEventHandler.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/EBusEventHandler.h index eb905b7c90..c6d4a8b512 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/EBusEventHandler.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/EBusEventHandler.h @@ -138,7 +138,11 @@ namespace ScriptCanvas void SetAutoConnectToGraphOwner(bool enabled); + void OnDeserialize(); + +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// void OnWriteEnd(); +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) AZStd::string GetNodeName() const override { diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp index 9701e346be..eb1ee501a9 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp @@ -313,6 +313,7 @@ namespace ScriptCanvas } PopulateNodeType(); + m_warnOnMissingFunction = true; } bool Method::InitializeOverloaded([[maybe_unused]] const NamespacePath& namespaces, AZStd::string_view className, AZStd::string_view methodName) @@ -739,10 +740,11 @@ namespace ScriptCanvas return TupleType{ nullptr, MethodType::Count, EventType::Count, nullptr }; } - void Method::OnWriteEnd() + void Method::OnDeserialize() { AZStd::lock_guard lock(m_mutex); + m_warnOnMissingFunction = true; const AZ::BehaviorClass* bcClass{}; const AZ::BehaviorMethod* method{}; EventType eventType; @@ -758,14 +760,22 @@ namespace ScriptCanvas { AZ_Warning("ScriptCanvas", !m_warnOnMissingFunction, "method node failed to deserialize properly"); } - } if (m_resultSlotIDs.empty()) { m_resultSlotIDs.emplace_back(SlotId{}); } + + Node::OnDeserialize(); + } + +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// + void Method::OnWriteEnd() + { + OnDeserialize(); } +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) bool Method::BranchesOnResult() const { @@ -829,7 +839,9 @@ namespace ScriptCanvas { serializeContext->Class() ->Version(MethodCPP::eVersion::Current, &MethodCPP::MethodVersionConverter) +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// ->EventHandler>() +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) ->Field("methodType", &Method::m_methodType) ->Field("methodName", &Method::m_lookupName) ->Field("className", &Method::m_className) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h index 41d4adcb84..5584212048 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h @@ -22,7 +22,6 @@ namespace AZ namespace ScriptCanvas { - AZ::Outcome IsExposable(const AZ::BehaviorMethod& method); Grammar::FunctionPrototype ToSignature(const AZ::BehaviorMethod& method); @@ -31,7 +30,8 @@ namespace ScriptCanvas { namespace Core { - class Method : public Node + class Method + : public Node { public: AZ_COMPONENT(Method, "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF}", Node); @@ -107,7 +107,11 @@ namespace ScriptCanvas SlotId GetBusSlotId() const; + void OnDeserialize(); + +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// void OnWriteEnd(); +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) virtual bool IsMethodOverloaded() const { return false; } @@ -180,7 +184,7 @@ namespace ScriptCanvas AZStd::vector m_inputSlots; AZStd::vector m_resultSlotIDs; AZStd::recursive_mutex m_mutex; // post-serialization - bool m_warnOnMissingFunction = true; + bool m_warnOnMissingFunction = false; Method(const Method&) = delete; }; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.cpp index c902d279bc..f49fcb3318 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.cpp @@ -60,7 +60,9 @@ namespace ScriptCanvas { serializeContext->Class() ->Version(MethodOverloadedCpp::Version::Current, &MethodOverloadedVersionConverter) +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// ->EventHandler>() +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) ->Field("orderedInputSlotIds", &MethodOverloaded::m_orderedInputSlotIds) ->Field("outputSlotIds", &MethodOverloaded::m_outputSlotIds) ; @@ -186,6 +188,7 @@ namespace ScriptCanvas RefreshActiveIndexes(); ConfigureContracts(); + SetWarnOnMissingFunction(true); } SlotId MethodOverloaded::AddMethodInputSlot(const MethodConfiguration& config, size_t argumentIndex) @@ -397,20 +400,14 @@ namespace ScriptCanvas return signature; } - void MethodOverloaded::OnReadBegin() - { - } - - void MethodOverloaded::OnReadEnd() - { - } - - void MethodOverloaded::OnWriteBegin() +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// + void MethodOverloaded::OnWriteEnd() { - SetWarnOnMissingFunction(false); + OnDeserialize(); } +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) - void MethodOverloaded::OnWriteEnd() + void MethodOverloaded::OnDeserialize() { AZStd::lock_guard lock(GetMutex()); @@ -462,6 +459,7 @@ namespace ScriptCanvas } SetWarnOnMissingFunction(true); + Node::OnDeserialize(); } void MethodOverloaded::SetupMethodData(const AZ::BehaviorMethod* behaviorMethod, const AZ::BehaviorClass* behaviorClass) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.h index c15b8f718c..b880f68862 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/MethodOverloaded.h @@ -33,7 +33,9 @@ namespace ScriptCanvas , public OverloadContractInterface { private: +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// friend class SerializeContextReadWriteHandler; +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) public: static void Reflect(AZ::ReflectContext* reflectContext); @@ -91,13 +93,15 @@ namespace ScriptCanvas // \todo make execution thread sensitive, which can then support generic programming Grammar::FunctionPrototype GetInputSignature() const; +#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)//// // SerializeContextReadWriteHandler - void OnReadBegin(); - void OnReadEnd(); - - void OnWriteBegin(); + void OnReadBegin() {} + void OnReadEnd() {} + void OnWriteBegin() {} void OnWriteEnd(); - //// +#endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) + + void OnDeserialize() override; void SetupMethodData(const AZ::BehaviorMethod* lookupMethod, const AZ::BehaviorClass* lookupClass); void ConfigureContracts(); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/Gate.ScriptCanvasGrammar.xml b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/Gate.ScriptCanvasGrammar.xml index 53d8623fe8..4e2e89fc7b 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/Gate.ScriptCanvasGrammar.xml +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Logic/Gate.ScriptCanvasGrammar.xml @@ -5,7 +5,6 @@ QualifiedName="ScriptCanvas::Nodes::Logic::Gate" PreferredClassName="If" Uuid="{F19CC10A-02FD-4E75-ADAA-9CFBD8A4E2F8}" - Base="ScriptCanvas::Node" Icon="Icons/ScriptCanvas/Print.png" Version="0" GeneratePropertyFriend="True" diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Serialization/BehaviorContextObjectSerializer.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Serialization/BehaviorContextObjectSerializer.cpp new file mode 100644 index 0000000000..0f965b012f --- /dev/null +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Serialization/BehaviorContextObjectSerializer.cpp @@ -0,0 +1,149 @@ +/* + * 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 +#include +#include +#include + +using namespace ScriptCanvas; + +namespace AZ +{ + AZ_CLASS_ALLOCATOR_IMPL(BehaviorContextObjectSerializer, SystemAllocator, 0); + + JsonSerializationResult::Result BehaviorContextObjectSerializer::Load + ( void* outputValue + , [[maybe_unused]] const Uuid& outputValueTypeId + , const rapidjson::Value& inputValue + , JsonDeserializerContext& context) + { + namespace JSR = JsonSerializationResult; + + AZ_Assert(outputValueTypeId == azrtti_typeid(), "BehaviorContextObjectSerializer Load against output typeID" + "that was not BehaviorContextObject"); + AZ_Assert(outputValue, "BehaviorContextObjectSerializer Load against null output"); + + JsonSerializationResult::ResultCode result(JSR::Tasks::ReadField); + auto outputBehaviorContextObject = reinterpret_cast(outputValue); + + bool isOwned = false; + result.Combine(ContinueLoadingFromJsonObjectField + ( &isOwned + , azrtti_typeid() + , inputValue + , "isOwned" + , context)); + + if (isOwned) + { + AZStd::any storage; + { // any storage begin + + auto typeIdMember = inputValue.FindMember(JsonSerialization::TypeIdFieldIdentifier); + if (typeIdMember == inputValue.MemberEnd()) + { + return context.Report + (JSR::Tasks::ReadField + , JSR::Outcomes::Missing + , AZStd::string::format("BehaviorContextObjectSerializer::Load failed to load the %s member" + , JsonSerialization::TypeIdFieldIdentifier)); + } + + AZ::Uuid typeId; + result.Combine(LoadTypeId(typeId, typeIdMember->value, context)); + if (typeId.IsNull()) + { + return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Catastrophic + , "BehaviorContextObjectSerializer::Load failed to load the AZ TypeId of the value"); + } + + storage = context.GetSerializeContext()->CreateAny(typeId); + if (storage.empty() || storage.type() != typeId) + { + return context.Report(result, "BehaviorContextObjectSerializer::Load failed to load a value matched the reported AZ " + "TypeId. The C++ declaration may have been deleted or changed."); + } + + result.Combine(ContinueLoadingFromJsonObjectField(AZStd::any_cast(&storage), typeId, inputValue, "value", context)); + } // any storage end + + auto bcClass = AZ::BehaviorContextHelper::GetClass(storage.type()); + BehaviorContextObject::Deserialize(*outputBehaviorContextObject, *bcClass, storage); + } + + return context.Report(result, result.GetProcessing() != JSR::Processing::Halted + ? "BehaviorContextObjectSerializer Load finished loading BehaviorContextObject" + : "BehaviorContextObjectSerializer Load failed to load BehaviorContextObject"); + } + + JsonSerializationResult::Result BehaviorContextObjectSerializer::Store + ( rapidjson::Value& outputValue + , const void* inputValue + , const void* defaultValue + , [[maybe_unused]] const Uuid& valueTypeId + , JsonSerializerContext& context) + { + namespace JSR = JsonSerializationResult; + + AZ_Assert(valueTypeId == azrtti_typeid(), "BehaviorContextObjectSerializer Store against value typeID that " + "was not BehaviorContextObject"); + AZ_Assert(inputValue, "BehaviorContextObjectSerializer Store against null inputValue pointer "); + + auto defaultScriptDataPtr = reinterpret_cast(defaultValue); + auto inputScriptDataPtr = reinterpret_cast(inputValue); + + if (defaultScriptDataPtr) + { + if (AZ::Helpers::CompareAnyValue(inputScriptDataPtr->ToAny(), defaultScriptDataPtr->ToAny())) + { + return context.Report + ( JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "BehaviorContextObjectSerializer Store used defaults " + "for BehaviorContextObject"); + } + } + + outputValue.SetObject(); + JSR::ResultCode result(JSR::Tasks::WriteValue); + const bool isInputOwned = inputScriptDataPtr->IsOwned(); + const bool isDefaultOwned = defaultScriptDataPtr ? defaultScriptDataPtr->IsOwned() : false; + result.Combine(ContinueStoringToJsonObjectField + ( outputValue + , "isOwned" + , &isInputOwned + , &isDefaultOwned + , azrtti_typeid() + , context)); + + if (isInputOwned) + { + // any storage begin + { + rapidjson::Value typeValue; + result.Combine(StoreTypeId(typeValue, inputScriptDataPtr->ToAny().type(), context)); + outputValue.AddMember + ( rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier) + , AZStd::move(typeValue) + , context.GetJsonAllocator()); + } + + result.Combine(ContinueStoringToJsonObjectField + ( outputValue + , "value" + , inputScriptDataPtr->Get() + , defaultScriptDataPtr ? defaultScriptDataPtr->Get() : nullptr + , inputScriptDataPtr->ToAny().type() + , context)); + // datum storage end + } + + return context.Report(result, result.GetProcessing() != JSR::Processing::Halted + ? "BehaviorContextObjectSerializer Store finished saving BehaviorContextObject" + : "BehaviorContextObjectSerializer Store failed to save BehaviorContextObject"); + } +} diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Serialization/BehaviorContextObjectSerializer.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Serialization/BehaviorContextObjectSerializer.h new file mode 100644 index 0000000000..012bb71680 --- /dev/null +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Serialization/BehaviorContextObjectSerializer.h @@ -0,0 +1,37 @@ +/* + * 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 +#include +#include + +namespace AZ +{ + class BehaviorContextObjectSerializer + : public BaseJsonSerializer + { + public: + AZ_RTTI(BehaviorContextObjectSerializer, "{88469C4C-923F-4508-A45C-33DDBB91074E}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR_DECL; + + private: + JsonSerializationResult::Result Load + ( void* outputValue + , const Uuid& outputValueTypeId + , const rapidjson::Value& inputValue + , JsonDeserializerContext& context) override; + + JsonSerializationResult::Result Store + ( rapidjson::Value& outputValue + , const void* inputValue + , const void* defaultValue + , const Uuid& valueTypeId, JsonSerializerContext& context) override; + }; +} diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Serialization/DatumSerializer.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Serialization/DatumSerializer.cpp index 44ca1730a8..5bfb69b11e 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Serialization/DatumSerializer.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Serialization/DatumSerializer.cpp @@ -50,10 +50,23 @@ namespace AZ , "scriptCanvasType" , context)); - AZStd::any storage; - { // datum storage begin - AZ::Uuid typeId = AZ::Uuid::CreateNull(); + // datum storage begin + auto isNullPointerMember = inputValue.FindMember("isNullPointer"); + if (isNullPointerMember == inputValue.MemberEnd()) + { + return context.Report + ( JSR::Tasks::ReadField + , JSR::Outcomes::Missing + , "DatumSerializer::Load failed to load the 'isNullPointer'' member"); + } + if (isNullPointerMember->value.GetBool()) + { + *outputDatum = Datum(scType, Datum::eOriginality::Original); + } + else + { + AZ::Uuid typeId = AZ::Uuid::CreateNull(); auto typeIdMember = inputValue.FindMember(JsonSerialization::TypeIdFieldIdentifier); if (typeIdMember == inputValue.MemberEnd()) { @@ -71,7 +84,7 @@ namespace AZ , "DatumSerializer::Load failed to load the AZ TypeId of the value"); } - storage = context.GetSerializeContext()->CreateAny(typeId); + AZStd::any storage = context.GetSerializeContext()->CreateAny(typeId); if (storage.empty() || storage.type() != typeId) { return context.Report(result, "DatumSerializer::Load failed to load a value matched the reported AZ TypeId. " @@ -79,22 +92,25 @@ namespace AZ } result.Combine(ContinueLoadingFromJsonObjectField(AZStd::any_cast(&storage), typeId, inputValue, "value", context)); + *outputDatum = Datum(scType, Datum::eOriginality::Original, AZStd::any_cast(&storage), scType.GetAZType()); } // datum storage end AZStd::string label; AZ_Assert(azrtti_typeidm_datumLabel)>() == azrtti_typeid() , "m_datumLabel type changed and won't load properly"); - result.Combine(ContinueLoadingFromJsonObjectField + result.Combine(ContinueLoadingFromJsonObjectField ( &label , azrtti_typeidm_datumLabel)>() , inputValue , "label" , context)); + outputDatum->SetLabel(label); - Datum copy(scType, Datum::eOriginality::Original, AZStd::any_cast(&storage), scType.GetAZType()); - copy.SetLabel(label); - *outputDatum = copy; - + if (auto listeners = context.GetMetadata().Find()) + { + listeners->push_back(outputDatum); + } + return context.Report(result, result.GetProcessing() != JSR::Processing::Halted ? "DatumSerializer Load finished loading Datum" : "DatumSerializer Load failed to load Datum"); @@ -123,7 +139,7 @@ namespace AZ ( JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "DatumSerializer Store used defaults for Datum"); } } - + JSR::ResultCode result(JSR::Tasks::WriteValue); outputValue.SetObject(); @@ -142,26 +158,32 @@ namespace AZ , defaultScriptDataPtr ? &defaultScriptDataPtr->GetType() : nullptr , azrtti_typeidGetType())>() , context)); - - { // datum storage begin - { - rapidjson::Value typeValue; - result.Combine(StoreTypeId(typeValue, inputScriptDataPtr->GetType().GetAZType(), context)); - outputValue.AddMember - ( rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier) - , AZStd::move(typeValue) - , context.GetJsonAllocator()); - } + + // datum storage begin + auto inputObjectSource = inputScriptDataPtr->GetAsDanger(); + outputValue.AddMember("isNullPointer", rapidjson::Value(inputObjectSource == nullptr), context.GetJsonAllocator()); + + if (inputObjectSource) + { + rapidjson::Value typeValue; + result.Combine(StoreTypeId(typeValue, inputScriptDataPtr->GetType().GetAZType(), context)); + outputValue.AddMember + ( rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier) + , AZStd::move(typeValue) + , context.GetJsonAllocator()); + + auto defaultObjectSource = defaultScriptDataPtr ? defaultScriptDataPtr->GetAsDanger() : nullptr; result.Combine(ContinueStoringToJsonObjectField ( outputValue , "value" - , inputScriptDataPtr->GetAsDanger() - , defaultScriptDataPtr ? defaultScriptDataPtr->GetAsDanger() : nullptr + , inputObjectSource + , defaultObjectSource , inputScriptDataPtr->GetType().GetAZType() , context)); - } // datum storage end - + } + // datum storage end + result.Combine(ContinueStoringToJsonObjectField ( outputValue , "label" @@ -174,5 +196,4 @@ namespace AZ ? "DatumSerializer Store finished saving Datum" : "DatumSerializer Store failed to save Datum"); } - } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Serialization/RuntimeVariableSerializer.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Serialization/RuntimeVariableSerializer.cpp index 763206df38..e03a5e4096 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Serialization/RuntimeVariableSerializer.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Serialization/RuntimeVariableSerializer.cpp @@ -50,6 +50,7 @@ namespace AZ } result.Combine(ContinueLoadingFromJsonObjectField(AZStd::any_cast(&outputVariable->value), typeId, inputValue, "value", context)); + return context.Report(result, result.GetProcessing() != JSR::Processing::Halted ? "RuntimeVariableSerializer Load finished loading RuntimeVariable" : "RuntimeVariableSerializer Load failed to load RuntimeVariable"); diff --git a/Gems/ScriptCanvas/Code/Source/SystemComponent.cpp b/Gems/ScriptCanvas/Code/Source/SystemComponent.cpp index 0a82b8cf2a..26af95fa8e 100644 --- a/Gems/ScriptCanvas/Code/Source/SystemComponent.cpp +++ b/Gems/ScriptCanvas/Code/Source/SystemComponent.cpp @@ -7,7 +7,6 @@ */ #include - #include #include #include @@ -23,8 +22,9 @@ #include #include #include -#include #include +#include +#include #include #include @@ -59,6 +59,7 @@ namespace ScriptCanvas { void SystemComponent::Reflect(AZ::ReflectContext* context) { + VersionData::Reflect(context); Nodeable::Reflect(context); ReflectLibraries(context); @@ -88,13 +89,9 @@ namespace ScriptCanvas if (AZ::JsonRegistrationContext* jsonContext = azrtti_cast(context)) { - jsonContext->Serializer() - ->HandlesType() - ; - - jsonContext->Serializer() - ->HandlesType() - ; + jsonContext->Serializer()->HandlesType(); + jsonContext->Serializer()->HandlesType(); + jsonContext->Serializer()->HandlesType(); } #if defined(SC_EXECUTION_TRACE_ENABLED) diff --git a/Gems/ScriptCanvas/Code/scriptcanvasgem_common_files.cmake b/Gems/ScriptCanvas/Code/scriptcanvasgem_common_files.cmake index f1215dd580..7984340b7a 100644 --- a/Gems/ScriptCanvas/Code/scriptcanvasgem_common_files.cmake +++ b/Gems/ScriptCanvas/Code/scriptcanvasgem_common_files.cmake @@ -74,6 +74,7 @@ set(FILES Include/ScriptCanvas/Core/NodeableNodeOverloaded.cpp Include/ScriptCanvas/Core/NodeableNodeOverloaded.h Include/ScriptCanvas/Core/NodeFunctionGeneric.h + Include/ScriptCanvas/Core/SerializationListener.h Include/ScriptCanvas/Core/Slot.cpp Include/ScriptCanvas/Core/Slot.h Include/ScriptCanvas/Core/SlotConfigurationDefaults.h @@ -539,6 +540,8 @@ set(FILES Include/ScriptCanvas/Profiler/Aggregator.cpp Include/ScriptCanvas/Profiler/DrillerEvents.h Include/ScriptCanvas/Profiler/DrillerEvents.cpp + Include/ScriptCanvas/Serialization/BehaviorContextObjectSerializer.h + Include/ScriptCanvas/Serialization/BehaviorContextObjectSerializer.cpp Include/ScriptCanvas/Serialization/DatumSerializer.h Include/ScriptCanvas/Serialization/DatumSerializer.cpp Include/ScriptCanvas/Serialization/RuntimeVariableSerializer.h diff --git a/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_PathologicalFlowOfControl.scriptcanvas b/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_PathologicalFlowOfControl.scriptcanvas index 54dbf2e35e..f2666232d8 100644 --- a/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_PathologicalFlowOfControl.scriptcanvas +++ b/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_PathologicalFlowOfControl.scriptcanvas @@ -1,11310 +1,7028 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "ScriptCanvasData", + "ClassData": { + "m_scriptCanvas": { + "Id": { + "id": 31194166714268 + }, + "Name": "LY_SC_UnitTest_PathologicalFlowOfControl", + "Components": { + "Component_[3557501584054890409]": { + "$type": "{4D755CA9-AB92-462C-B24F-0B3376F19967} Graph", + "Id": 3557501584054890409, + "m_graphData": { + "m_nodes": [ + { + "Id": { + "id": 31288655994780 + }, + "Name": "EBusEventHandler", + "Components": { + "Component_[10251308001417185158]": { + "$type": "EBusEventHandler", + "Id": 10251308001417185158, + "Slots": [ + { + "id": { + "m_id": "{C189EE08-DC89-4AF2-88BC-B564F84F28A6}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Connect", + "toolTip": "Connect this event handler to the specified entity.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{10BB0A14-C955-464E-8745-7FBC88E24049}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Disconnect", + "toolTip": "Disconnect this event handler.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{A7026536-ED61-48CA-8329-601DACF8A178}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "OnConnected", + "toolTip": "Signaled when a connection has taken place.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{95E02974-9E1C-407C-94DA-087C5B4EC55C}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "OnDisconnected", + "toolTip": "Signaled when this event handler is disconnected.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{42C5EF09-0843-4478-8BFF-0401009FACEA}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "OnFailure", + "toolTip": "Signaled when it is not possible to connect this handler.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{CC23FBA6-EBA5-43DF-AB98-3024D4A01B21}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Number", + "DisplayDataType": { + "m_type": 3 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{D85921A2-82C6-4DE1-8DB0-DC93E80CD72B}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "ScriptTimePoint", + "DisplayDataType": { + "m_type": 4, + "m_azType": "{4C0F6AD4-0D4F-4354-AD4A-0C01E948245C}" + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{99FA955C-7E6E-4539-85B0-16CBFBB1F0E3}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "ExecutionSlot:OnTick", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + }, + "IsLatent": true + }, + { + "id": { + "m_id": "{3F00BC02-DBFB-4D4C-B5FD-4BE1C831E7AF}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Result: Number", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{3BAF3F8C-54A2-4AD7-82F2-9EBB4AEF48EF}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "ExecutionSlot:GetTickOrder", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + }, + "IsLatent": true + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Result: Number" + } + ], + "m_eventMap": [ + { + "Key": { + "Value": 1502188240 + }, + "Value": { + "m_eventName": "OnTick", + "m_eventId": { + "Value": 1502188240 + }, + "m_eventSlotId": { + "m_id": "{99FA955C-7E6E-4539-85B0-16CBFBB1F0E3}" + }, + "m_parameterSlotIds": [ + { + "m_id": "{CC23FBA6-EBA5-43DF-AB98-3024D4A01B21}" + }, + { + "m_id": "{D85921A2-82C6-4DE1-8DB0-DC93E80CD72B}" + } + ], + "m_numExpectedArguments": 2 + } + }, + { + "Key": { + "Value": 1890826333 + }, + "Value": { + "m_eventName": "GetTickOrder", + "m_eventId": { + "Value": 1890826333 + }, + "m_eventSlotId": { + "m_id": "{3BAF3F8C-54A2-4AD7-82F2-9EBB4AEF48EF}" + }, + "m_resultSlotId": { + "m_id": "{3F00BC02-DBFB-4D4C-B5FD-4BE1C831E7AF}" + } + } + } + ], + "m_ebusName": "TickBus", + "m_busId": { + "Value": 1209186864 + }, + "m_autoConnectToGraphOwner": false + } + } + }, + { + "Id": { + "id": 31314425798556 + }, + "Name": "EBusEventHandler", + "Components": { + "Component_[10251308001417185158]": { + "$type": "EBusEventHandler", + "Id": 10251308001417185158, + "Slots": [ + { + "id": { + "m_id": "{C189EE08-DC89-4AF2-88BC-B564F84F28A6}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Connect", + "toolTip": "Connect this event handler to the specified entity.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{10BB0A14-C955-464E-8745-7FBC88E24049}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Disconnect", + "toolTip": "Disconnect this event handler.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{A7026536-ED61-48CA-8329-601DACF8A178}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "OnConnected", + "toolTip": "Signaled when a connection has taken place.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{95E02974-9E1C-407C-94DA-087C5B4EC55C}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "OnDisconnected", + "toolTip": "Signaled when this event handler is disconnected.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{42C5EF09-0843-4478-8BFF-0401009FACEA}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "OnFailure", + "toolTip": "Signaled when it is not possible to connect this handler.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{CC23FBA6-EBA5-43DF-AB98-3024D4A01B21}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Number", + "DisplayDataType": { + "m_type": 3 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{D85921A2-82C6-4DE1-8DB0-DC93E80CD72B}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "ScriptTimePoint", + "DisplayDataType": { + "m_type": 4, + "m_azType": "{4C0F6AD4-0D4F-4354-AD4A-0C01E948245C}" + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{99FA955C-7E6E-4539-85B0-16CBFBB1F0E3}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "ExecutionSlot:OnTick", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + }, + "IsLatent": true + }, + { + "id": { + "m_id": "{3F00BC02-DBFB-4D4C-B5FD-4BE1C831E7AF}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Result: Number", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{3BAF3F8C-54A2-4AD7-82F2-9EBB4AEF48EF}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "ExecutionSlot:GetTickOrder", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + }, + "IsLatent": true + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Result: Number" + } + ], + "m_eventMap": [ + { + "Key": { + "Value": 1502188240 + }, + "Value": { + "m_eventName": "OnTick", + "m_eventId": { + "Value": 1502188240 + }, + "m_eventSlotId": { + "m_id": "{99FA955C-7E6E-4539-85B0-16CBFBB1F0E3}" + }, + "m_parameterSlotIds": [ + { + "m_id": "{CC23FBA6-EBA5-43DF-AB98-3024D4A01B21}" + }, + { + "m_id": "{D85921A2-82C6-4DE1-8DB0-DC93E80CD72B}" + } + ], + "m_numExpectedArguments": 2 + } + }, + { + "Key": { + "Value": 1890826333 + }, + "Value": { + "m_eventName": "GetTickOrder", + "m_eventId": { + "Value": 1890826333 + }, + "m_eventSlotId": { + "m_id": "{3BAF3F8C-54A2-4AD7-82F2-9EBB4AEF48EF}" + }, + "m_resultSlotId": { + "m_id": "{3F00BC02-DBFB-4D4C-B5FD-4BE1C831E7AF}" + } + } + } + ], + "m_ebusName": "TickBus", + "m_busId": { + "Value": 1209186864 + }, + "m_autoConnectToGraphOwner": false + } + } + }, + { + "Id": { + "id": 31211346583452 + }, + "Name": "SC-Node(Or)", + "Components": { + "Component_[10667361034986907134]": { + "$type": "Or", + "Id": 10667361034986907134, + "Slots": [ + { + "id": { + "m_id": "{78999179-DFFF-450B-B66E-A4C4E1638F0C}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Result", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{C5E1B10E-FDFA-45F8-B6AF-BD9C672986FC}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "Signal to perform the evaluation when desired.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{7FED3519-4670-4B3D-B81F-44393C2D7787}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "True", + "toolTip": "Signaled if the result of the operation is true.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{E2D1192C-902D-4C82-9DE4-BBC20B31A54B}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "False", + "toolTip": "Signaled if the result of the operation is false.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{E8D87D3B-0974-4937-BEC5-8AE3BED5EBC2}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Value A", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{56CECF75-DEC3-4491-85E6-7C0CE4B950D4}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Value B", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 0 + }, + "isNullPointer": false, + "$type": "bool", + "value": false, + "label": "Value A" + }, + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 0 + }, + "isNullPointer": false, + "$type": "bool", + "value": false, + "label": "Value B" + } + ] + } + } + }, + { + "Id": { + "id": 31267181158300 + }, + "Name": "SC-Node(OperatorAdd)", + "Components": { + "Component_[10710360517941107527]": { + "$type": "OperatorAdd", + "Id": 10710360517941107527, + "Slots": [ + { + "id": { + "m_id": "{CDEA58A6-1B59-4476-B26E-0D5A828F35C7}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{5E66E5C1-AE26-4919-BFD7-66EC6DE9B7BB}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{6EA79D99-58BC-485B-8015-3B85753F522A}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Number", + "toolTip": "An operand to use in performing the specified Operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{0BA96D88-BF13-4810-B01C-CA513AF84440}" + } + }, + { + "id": { + "m_id": "{A0CEC830-6910-452B-B850-75B6F727C455}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Number", + "toolTip": "An operand to use in performing the specified Operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{5AAA51D3-6C26-4D13-91E2-4D2A8B33C7F0}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Result", + "toolTip": "The result of the specified operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Number" + }, + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Number" + } + ] + } + } + }, + { + "Id": { + "id": 31305835863964 + }, + "Name": "SC-Node(OperatorAdd)", + "Components": { + "Component_[10710360517941107527]": { + "$type": "OperatorAdd", + "Id": 10710360517941107527, + "Slots": [ + { + "id": { + "m_id": "{CDEA58A6-1B59-4476-B26E-0D5A828F35C7}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{5E66E5C1-AE26-4919-BFD7-66EC6DE9B7BB}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{6EA79D99-58BC-485B-8015-3B85753F522A}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Number", + "toolTip": "An operand to use in performing the specified Operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{0BA96D88-BF13-4810-B01C-CA513AF84440}" + } + }, + { + "id": { + "m_id": "{A0CEC830-6910-452B-B850-75B6F727C455}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Number", + "toolTip": "An operand to use in performing the specified Operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{5AAA51D3-6C26-4D13-91E2-4D2A8B33C7F0}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Result", + "toolTip": "The result of the specified operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Number" + }, + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Number" + } + ] + } + } + }, + { + "Id": { + "id": 31228526452636 + }, + "Name": "SC-Node(And)", + "Components": { + "Component_[11788551755091650733]": { + "$type": "And", + "Id": 11788551755091650733, + "Slots": [ + { + "id": { + "m_id": "{46CFA92C-E217-41E2-BC0A-C7C4BA7D3060}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Result", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{FAFAB55A-69F1-495D-82FC-7D36F7842DA9}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "Signal to perform the evaluation when desired.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{5BDD309F-0047-43E9-A911-475E47A036EC}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "True", + "toolTip": "Signaled if the result of the operation is true.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{7438C80C-9044-442D-8C71-827AB7DC3F0B}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "False", + "toolTip": "Signaled if the result of the operation is false.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{0D33ACC4-1331-4F0C-AA29-8863E5312983}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Value A", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{E3D62E2D-145C-4931-9FC8-CFE6CEB2FF64}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Value B", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 0 + }, + "isNullPointer": false, + "$type": "bool", + "value": false, + "label": "Value A" + }, + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 0 + }, + "isNullPointer": false, + "$type": "bool", + "value": false, + "label": "Value B" + } + ] + } + } + }, + { + "Id": { + "id": 31232821419932 + }, + "Name": "SC-Node(ExtractProperty)", + "Components": { + "Component_[11797457255651779243]": { + "$type": "ExtractProperty", + "Id": 11797457255651779243, + "Slots": [ + { + "id": { + "m_id": "{EADFB5B4-ADE0-4935-A102-99F8EC0C711A}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "When signaled assigns property values using the supplied source input", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{AC7594DE-A7F0-4519-9E07-0350AEEB25E0}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "toolTip": "Signaled after all property haves have been pushed to the output slots", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{0C6BA77F-004F-4229-963F-EF6D6912A5BB}" + }, + "DynamicTypeOverride": 1, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Source", + "toolTip": "The value on which to extract properties from.", + "DisplayDataType": { + "m_type": 3 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{0BA96D88-BF13-4810-B01C-CA513AF84440}" + } + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Source" + } + ], + "m_dataType": { + "m_type": 3 + } + } + } + }, + { + "Id": { + "id": 31301540896668 + }, + "Name": "SC-Node(ExtractProperty)", + "Components": { + "Component_[11797457255651779243]": { + "$type": "ExtractProperty", + "Id": 11797457255651779243, + "Slots": [ + { + "id": { + "m_id": "{EADFB5B4-ADE0-4935-A102-99F8EC0C711A}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "When signaled assigns property values using the supplied source input", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{AC7594DE-A7F0-4519-9E07-0350AEEB25E0}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "toolTip": "Signaled after all property haves have been pushed to the output slots", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{0C6BA77F-004F-4229-963F-EF6D6912A5BB}" + }, + "DynamicTypeOverride": 1, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Source", + "toolTip": "The value on which to extract properties from.", + "DisplayDataType": { + "m_type": 3 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{0BA96D88-BF13-4810-B01C-CA513AF84440}" + } + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Source" + } + ], + "m_dataType": { + "m_type": 3 + } + } + } + }, + { + "Id": { + "id": 31224231485340 + }, + "Name": "SC-Node(ForEach)", + "Components": { + "Component_[11912058231419650479]": { + "$type": "ForEach", + "Id": 11912058231419650479, + "Slots": [ + { + "id": { + "m_id": "{B107E73D-A250-4019-BBF7-9085AEE354B6}" + }, + "DynamicTypeOverride": 2, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Source", + "DisplayDataType": { + "m_type": 4, + "m_azType": "{B505B05D-57BF-5A45-9113-B3A2784E2CD7}" + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 3089028177 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{39DB51CB-AA6E-4B3A-93EC-D622C44B3F4B}" + } + }, + { + "id": { + "m_id": "{A9E6B254-446D-492F-B2CF-929CDC04ABFF}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "Signaled upon node entry", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{B24CD5F3-DE2B-42E9-BD45-0BCFF5A3D4B9}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Break", + "toolTip": "Stops the iteration when signaled", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{8A4B7BED-4921-4C08-B35B-56D129A630E9}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Each", + "toolTip": "Signalled after each element of the container", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{0DEFFE74-9D9C-4E18-AFA2-12B1657F7D1D}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Finished", + "toolTip": "The container has been fully iterated over", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{EDA1A957-FC80-4305-9797-1CC2699D13F8}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Boolean", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 4, + "m_azType": "{B505B05D-57BF-5A45-9113-B3A2784E2CD7}" + }, + "isNullPointer": true, + "label": "Source" + } + ], + "m_sourceSlot": { + "m_id": "{B107E73D-A250-4019-BBF7-9085AEE354B6}" + }, + "m_previousTypeId": "{B505B05D-57BF-5A45-9113-B3A2784E2CD7}", + "m_propertySlots": [ + { + "m_propertySlotId": { + "m_id": "{EDA1A957-FC80-4305-9797-1CC2699D13F8}" + }, + "m_propertyType": { + "m_type": 0 + }, + "m_propertyName": "Boolean" + } + ] + } + } + }, + { + "Id": { + "id": 31284361027484 + }, + "Name": "SC-Node(ForEach)", + "Components": { + "Component_[11912058231419650479]": { + "$type": "ForEach", + "Id": 11912058231419650479, + "Slots": [ + { + "id": { + "m_id": "{B107E73D-A250-4019-BBF7-9085AEE354B6}" + }, + "DynamicTypeOverride": 2, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Source", + "DisplayDataType": { + "m_type": 4, + "m_azType": "{B505B05D-57BF-5A45-9113-B3A2784E2CD7}" + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 3089028177 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{39DB51CB-AA6E-4B3A-93EC-D622C44B3F4B}" + } + }, + { + "id": { + "m_id": "{A9E6B254-446D-492F-B2CF-929CDC04ABFF}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "Signaled upon node entry", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{B24CD5F3-DE2B-42E9-BD45-0BCFF5A3D4B9}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Break", + "toolTip": "Stops the iteration when signaled", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{8A4B7BED-4921-4C08-B35B-56D129A630E9}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Each", + "toolTip": "Signalled after each element of the container", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{0DEFFE74-9D9C-4E18-AFA2-12B1657F7D1D}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Finished", + "toolTip": "The container has been fully iterated over", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{EDA1A957-FC80-4305-9797-1CC2699D13F8}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Boolean", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 4, + "m_azType": "{B505B05D-57BF-5A45-9113-B3A2784E2CD7}" + }, + "isNullPointer": true, + "label": "Source" + } + ], + "m_sourceSlot": { + "m_id": "{B107E73D-A250-4019-BBF7-9085AEE354B6}" + }, + "m_previousTypeId": "{B505B05D-57BF-5A45-9113-B3A2784E2CD7}", + "m_propertySlots": [ + { + "m_propertySlotId": { + "m_id": "{EDA1A957-FC80-4305-9797-1CC2699D13F8}" + }, + "m_propertyType": { + "m_type": 0 + }, + "m_propertyName": "Boolean" + } + ] + } + } + }, + { + "Id": { + "id": 31215641550748 + }, + "Name": "SC-Node(OperatorMul)", + "Components": { + "Component_[13237430188878581756]": { + "$type": "OperatorMul", + "Id": 13237430188878581756, + "Slots": [ + { + "id": { + "m_id": "{2D2F75D9-A1FB-4D70-86EF-8518E119DB07}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{7A580403-C074-4DAD-A65E-BF928EF19DC8}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{CD4A3CE3-F0A5-4E82-BC6D-1AD9F2596A7E}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "MathOperatorContract", + "OperatorType": "Multiply", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 7 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Number", + "toolTip": "An operand to use in performing the specified Operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{0BA96D88-BF13-4810-B01C-CA513AF84440}" + } + }, + { + "id": { + "m_id": "{D662CA74-64EF-4D8F-BC80-D222A4990F3E}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "MathOperatorContract", + "OperatorType": "Multiply", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 7 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Number", + "toolTip": "An operand to use in performing the specified Operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{EC0D7AE5-C51F-48EE-BD42-10712F131C57}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + { + "$type": "MathOperatorContract", + "OperatorType": "Multiply", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 7 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Result", + "toolTip": "The result of the specified operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Number" + }, + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 1.0, + "label": "Number" + } + ] + } + } + }, + { + "Id": { + "id": 31202756648860 + }, + "Name": "SC-Node(MarkComplete)", + "Components": { + "Component_[13763264706969284951]": { + "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method", + "Id": 13763264706969284951, + "Slots": [ + { + "isVisibile": false, + "id": { + "m_id": "{A438327C-87F6-43C3-A1E2-C4BDAADA8C5D}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "EntityID: 0", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{D2BCC38E-DA18-41ED-9B80-DB5602A9DB26}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Report", + "toolTip": "additional notes for the test report", + "DisplayDataType": { + "m_type": 5 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{79C2ABDD-F20A-4042-BF5C-8C09DC9332C7}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{11965287-6D28-49B7-B387-3728356C2E0D}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 1 + }, + "isNullPointer": false, + "$type": "EntityId", + "value": { + "id": 4276206253 + } + }, + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 5 + }, + "isNullPointer": false, + "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", + "value": "", + "label": "Report" + } + ], + "methodType": 2, + "methodName": "Mark Complete", + "className": "Unit Testing", + "resultSlotIDs": [ + {} + ], + "prettyClassName": "Unit Testing" + } + } + }, + { + "Id": { + "id": 31280066060188 + }, + "Name": "SC-Node(Not)", + "Components": { + "Component_[17238549219073370854]": { + "$type": "Not", + "Id": 17238549219073370854, + "Slots": [ + { + "id": { + "m_id": "{54D7FBC6-7B2A-4A3E-AD8C-F28A7C4C353D}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Value", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{41EB5D06-DA39-434B-81D5-3E82A375C897}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Result", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{120C810C-F84A-445F-82E1-FD06CD3AD9B7}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "Signal to perform the evaluation when desired.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{6E82DFA3-70F4-419F-9971-AD243313E8D9}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "True", + "toolTip": "Signaled if the result of the operation is true.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{5C53A008-E808-4D00-A4EC-AC6A0EFDAFB9}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "False", + "toolTip": "Signaled if the result of the operation is false.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 0 + }, + "isNullPointer": false, + "$type": "bool", + "value": false, + "label": "Value" + } + ] + } + } + }, + { + "Id": { + "id": 31258591223708 + }, + "Name": "SC-Node(TargetedSequencer)", + "Components": { + "Component_[2375509582066284148]": { + "$type": "TargetedSequencer", + "Id": 2375509582066284148, + "Slots": [ + { + "id": { + "m_id": "{52F5A723-7921-4644-AB19-5DCB94B51494}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{800E14B1-C788-4C7F-B4D4-76121DE547C9}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 0", + "toolTip": "Output 0", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{14CCF4FA-A3F5-41F7-B66C-BCFF9D5A61F8}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Index", + "toolTip": "Select which [Out#] to trigger.", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{9828EDD9-2689-424C-9708-304D720B1348}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 1", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{4A1E25C6-E63B-4BC0-BBDE-533CA69C91C5}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 2", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Index" + } + ] + } + } + }, + { + "Id": { + "id": 31297245929372 + }, + "Name": "SC-Node(TargetedSequencer)", + "Components": { + "Component_[2375509582066284148]": { + "$type": "TargetedSequencer", + "Id": 2375509582066284148, + "Slots": [ + { + "id": { + "m_id": "{52F5A723-7921-4644-AB19-5DCB94B51494}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{800E14B1-C788-4C7F-B4D4-76121DE547C9}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 0", + "toolTip": "Output 0", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{14CCF4FA-A3F5-41F7-B66C-BCFF9D5A61F8}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Index", + "toolTip": "Select which [Out#] to trigger.", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{9828EDD9-2689-424C-9708-304D720B1348}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 1", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{4A1E25C6-E63B-4BC0-BBDE-533CA69C91C5}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 2", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Index" + } + ] + } + } + }, + { + "Id": { + "id": 31310130831260 + }, + "Name": "SC-Node(OperatorAdd)", + "Components": { + "Component_[4806888795455241210]": { + "$type": "OperatorAdd", + "Id": 4806888795455241210, + "Slots": [ + { + "id": { + "m_id": "{792F23CB-7356-4DCA-A186-69F2A0BBC063}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{775F77BE-A9EC-455D-B378-15D16A0BEA4F}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{02291180-ECAE-4D8B-8A9D-F9E451A3416F}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Number", + "toolTip": "An operand to use in performing the specified Operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{0BA96D88-BF13-4810-B01C-CA513AF84440}" + } + }, + { + "id": { + "m_id": "{CCC2E84B-A90B-460B-AD1A-A2CCFF94D56F}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Number", + "toolTip": "An operand to use in performing the specified Operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{0BA96D88-BF13-4810-B01C-CA513AF84440}" + } + }, + { + "id": { + "m_id": "{40B63F5F-29F6-4F92-9C11-E47653798827}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + { + "$type": "MathOperatorContract", + "NativeTypes": [ + { + "m_type": 3 + }, + { + "m_type": 6 + }, + { + "m_type": 8 + }, + { + "m_type": 9 + }, + { + "m_type": 10 + }, + { + "m_type": 11 + }, + { + "m_type": 12 + }, + { + "m_type": 14 + }, + { + "m_type": 15 + } + ] + } + ], + "slotName": "Result", + "toolTip": "The result of the specified operation", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1114760223 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DynamicGroup": { + "Value": 1114760223 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Number" + }, + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Number" + } + ] + } + } + }, + { + "Id": { + "id": 31198461681564 + }, + "Name": "SC-Node((NodeFunctionGenericMultiReturn)<{bool(Vector3 double )}* IsZeroTraits >)", + "Components": { + "Component_[5026503393630088694]": { + "$type": "(NodeFunctionGenericMultiReturn)<{bool(Vector3 double )}* IsZeroTraits >", + "Id": 5026503393630088694, + "Slots": [ + { + "id": { + "m_id": "{048F79E8-478C-46A1-AF3F-0B578022240D}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{2CD5D252-4872-41A2-9C9E-4B81AD9877B7}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{765E9D80-9091-400F-858D-1C70FA1C25B2}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Vector3: Source", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{33BC5FB6-AB79-4121-8C82-0F0EFE4FB7F5}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Number: Tolerance", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{C30EA842-3E60-4B71-A5C9-D70BB9FACC53}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Result: Boolean", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 8 + }, + "isNullPointer": false, + "$type": "Vector3", + "value": [ + 0.0, + 0.0, + 0.0 + ], + "label": "Vector3: Source" + }, + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 1e-7, + "label": "Number: Tolerance" + } + ], + "Initialized": true + } + } + }, + { + "Id": { + "id": 31207051616156 + }, + "Name": "SC-Node((NodeFunctionGenericMultiReturn)<{bool(Vector3 double )}* IsZeroTraits >)", + "Components": { + "Component_[5026503393630088694]": { + "$type": "(NodeFunctionGenericMultiReturn)<{bool(Vector3 double )}* IsZeroTraits >", + "Id": 5026503393630088694, + "Slots": [ + { + "id": { + "m_id": "{048F79E8-478C-46A1-AF3F-0B578022240D}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{2CD5D252-4872-41A2-9C9E-4B81AD9877B7}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{765E9D80-9091-400F-858D-1C70FA1C25B2}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Vector3: Source", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{33BC5FB6-AB79-4121-8C82-0F0EFE4FB7F5}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "Number: Tolerance", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{C30EA842-3E60-4B71-A5C9-D70BB9FACC53}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Result: Boolean", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 8 + }, + "isNullPointer": false, + "$type": "Vector3", + "value": [ + 0.0, + 0.0, + 0.0 + ], + "label": "Vector3: Source" + }, + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 1e-7, + "label": "Number: Tolerance" + } + ], + "Initialized": true + } + } + }, + { + "Id": { + "id": 31219936518044 + }, + "Name": "SC-Node(MathExpression)", + "Components": { + "Component_[5840825893583504722]": { + "$type": "MathExpression", + "Id": 5840825893583504722, + "Slots": [ + { + "id": { + "m_id": "{6F8E4206-72C6-40E6-8AC1-205AA2C04804}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "Input signal", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{5E038FBC-9A4B-489C-AB3E-26F103B7102E}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "toolTip": "Output signal", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{7E74B9C7-C879-4B82-965F-889F8824BDFC}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "x", + "toolTip": "Value which replaces instances of {x} in the resulting expression.", + "DisplayGroup": { + "Value": 1997398926 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{28E4C6B8-0099-4213-BA4A-C21CC6EC0A5E}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "y", + "toolTip": "Value which replaces instances of {y} in the resulting expression.", + "DisplayGroup": { + "Value": 1997398926 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{AE1904C5-683D-40CA-93F6-5F18D40512BF}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Result", + "toolTip": "The resulting string.", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1997398926 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 3.0, + "label": "x" + }, + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 4.0, + "label": "y" + } + ], + "m_format": "{x} + {y}", + "m_expressionTree": { + "Variables": [ + { + "Key": { + "Value": 2363233923 + }, + "Value": { + "SupportedTypes": [ + "{110C4B14-11A8-4E9D-8638-5051013A56AC}" + ], + "$type": "double", + "Value": 3.0 + } + }, + { + "Key": { + "Value": 4225443349 + }, + "Value": { + "SupportedTypes": [ + "{110C4B14-11A8-4E9D-8638-5051013A56AC}" + ], + "$type": "double", + "Value": 4.0 + } + } + ], + "VariableDisplayOrder": [ + "x", + "y" + ], + "Tokens": [ + { + "TokenInformation": { + "Id": 1, + "$type": "{6D219DB1-3763-4408-A3E8-75E4AE66E9BD} VariableDescriptor", + "Value": { + "DisplayName": "x", + "NameHash": { + "Value": 2363233923 + } + } + } + }, + { + "TokenInformation": { + "Id": 1, + "$type": "{6D219DB1-3763-4408-A3E8-75E4AE66E9BD} VariableDescriptor", + "Value": { + "DisplayName": "y", + "NameHash": { + "Value": 4225443349 + } + } + } + }, + { + "ParserId": 3499457960, + "TokenInformation": { + "Id": 0, + "$type": "Empty AZStd::any" + } + } + ] + } + } + } + }, + { + "Id": { + "id": 31241411354524 + }, + "Name": "SC-Node(MathExpression)", + "Components": { + "Component_[5840825893583504722]": { + "$type": "MathExpression", + "Id": 5840825893583504722, + "Slots": [ + { + "id": { + "m_id": "{6F8E4206-72C6-40E6-8AC1-205AA2C04804}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "Input signal", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{5E038FBC-9A4B-489C-AB3E-26F103B7102E}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "toolTip": "Output signal", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{7E74B9C7-C879-4B82-965F-889F8824BDFC}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "x", + "toolTip": "Value which replaces instances of {x} in the resulting expression.", + "DisplayGroup": { + "Value": 1997398926 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{28E4C6B8-0099-4213-BA4A-C21CC6EC0A5E}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null + ], + "slotName": "y", + "toolTip": "Value which replaces instances of {y} in the resulting expression.", + "DisplayGroup": { + "Value": 1997398926 + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{AE1904C5-683D-40CA-93F6-5F18D40512BF}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Result", + "toolTip": "The resulting string.", + "DisplayDataType": { + "m_type": 3 + }, + "DisplayGroup": { + "Value": 1997398926 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 3.0, + "label": "x" + }, + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 4.0, + "label": "y" + } + ], + "m_format": "{x} + {y}", + "m_expressionTree": { + "Variables": [ + { + "Key": { + "Value": 2363233923 + }, + "Value": { + "SupportedTypes": [ + "{110C4B14-11A8-4E9D-8638-5051013A56AC}" + ], + "$type": "double", + "Value": 3.0 + } + }, + { + "Key": { + "Value": 4225443349 + }, + "Value": { + "SupportedTypes": [ + "{110C4B14-11A8-4E9D-8638-5051013A56AC}" + ], + "$type": "double", + "Value": 4.0 + } + } + ], + "VariableDisplayOrder": [ + "x", + "y" + ], + "Tokens": [ + { + "TokenInformation": { + "Id": 1, + "$type": "{6D219DB1-3763-4408-A3E8-75E4AE66E9BD} VariableDescriptor", + "Value": { + "DisplayName": "x", + "NameHash": { + "Value": 2363233923 + } + } + } + }, + { + "TokenInformation": { + "Id": 1, + "$type": "{6D219DB1-3763-4408-A3E8-75E4AE66E9BD} VariableDescriptor", + "Value": { + "DisplayName": "y", + "NameHash": { + "Value": 4225443349 + } + } + } + }, + { + "ParserId": 3499457960, + "TokenInformation": { + "Id": 0, + "$type": "Empty AZStd::any" + } + } + ] + } + } + } + }, + { + "Id": { + "id": 31292950962076 + }, + "Name": "SC-Node(OrderedSequencer)", + "Components": { + "Component_[5989899372375578125]": { + "$type": "OrderedSequencer", + "Id": 5989899372375578125, + "Slots": [ + { + "id": { + "m_id": "{E4E25252-6AE7-4A4F-A175-50B9C6CD5DAE}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{425822FB-C61E-4493-98C3-3A05418BD029}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 0", + "toolTip": "Output 0", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ] + } + } + }, + { + "Id": { + "id": 31275771092892 + }, + "Name": "SC-Node(OrderedSequencer)", + "Components": { + "Component_[5989899372375578125]": { + "$type": "OrderedSequencer", + "Id": 5989899372375578125, + "Slots": [ + { + "id": { + "m_id": "{E4E25252-6AE7-4A4F-A175-50B9C6CD5DAE}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{425822FB-C61E-4493-98C3-3A05418BD029}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 0", + "toolTip": "Output 0", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{8FDC2E61-37AE-4E0F-82FE-7A4D9696123C}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 1", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{1000AB91-55FB-4615-B0EE-306816CFE895}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 2", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ] + } + } + }, + { + "Id": { + "id": 31250001289116 + }, + "Name": "SC-Node(IsNull)", + "Components": { + "Component_[6380535138172294808]": { + "$type": "IsNull", + "Id": 6380535138172294808, + "Slots": [ + { + "id": { + "m_id": "{AEAA495A-12CF-42CC-80FA-B4D67AB5EA99}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "IsReferenceTypeContract" + } + ], + "slotName": "Reference", + "DisplayDataType": { + "m_type": 4, + "m_azType": "{B505B05D-57BF-5A45-9113-B3A2784E2CD7}" + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{39DB51CB-AA6E-4B3A-93EC-D622C44B3F4B}" + } + }, + { + "id": { + "m_id": "{C17691F7-094B-4663-B2D6-F7726B8D14D2}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Is Null", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{2215C7E1-3EA3-4487-BA74-0A5628424CEA}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "Input signal", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{AA375295-D5E8-473C-A073-08A2853E750E}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "True", + "toolTip": "Signaled if the reference provided is null.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{342C520C-CA78-4261-91C9-629978B79BA3}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "False", + "toolTip": "Signaled if the reference provided is not null.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 4, + "m_azType": "{B505B05D-57BF-5A45-9113-B3A2784E2CD7}" + }, + "isNullPointer": true, + "label": "Reference" + } + ] + } + } + }, + { + "Id": { + "id": 31254296256412 + }, + "Name": "SC-Node(IsNull)", + "Components": { + "Component_[6380535138172294808]": { + "$type": "IsNull", + "Id": 6380535138172294808, + "Slots": [ + { + "id": { + "m_id": "{AEAA495A-12CF-42CC-80FA-B4D67AB5EA99}" + }, + "DynamicTypeOverride": 3, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + null, + { + "$type": "IsReferenceTypeContract" + } + ], + "slotName": "Reference", + "DisplayDataType": { + "m_type": 4, + "m_azType": "{B505B05D-57BF-5A45-9113-B3A2784E2CD7}" + }, + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1, + "IsReference": true, + "VariableReference": { + "m_id": "{39DB51CB-AA6E-4B3A-93EC-D622C44B3F4B}" + } + }, + { + "id": { + "m_id": "{C17691F7-094B-4663-B2D6-F7726B8D14D2}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Is Null", + "DisplayDataType": { + "m_type": 0 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{2215C7E1-3EA3-4487-BA74-0A5628424CEA}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "Input signal", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{AA375295-D5E8-473C-A073-08A2853E750E}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "True", + "toolTip": "Signaled if the reference provided is null.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{342C520C-CA78-4261-91C9-629978B79BA3}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "False", + "toolTip": "Signaled if the reference provided is not null.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 4, + "m_azType": "{B505B05D-57BF-5A45-9113-B3A2784E2CD7}" + }, + "isNullPointer": true, + "label": "Reference" + } + ] + } + } + }, + { + "Id": { + "id": 31262886191004 + }, + "Name": "SC-Node(Start)", + "Components": { + "Component_[6709400654876708818]": { + "$type": "Start", + "Id": 6709400654876708818, + "Slots": [ + { + "id": { + "m_id": "{E1417C09-E03B-4EA9-B9EC-67F8EC862B69}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "toolTip": "Signaled when the entity that owns this graph is fully activated.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ] + } + } + }, + { + "Id": { + "id": 31271476125596 + }, + "Name": "SC-Node(Once)", + "Components": { + "Component_[7505935143439181025]": { + "$type": "Once", + "Id": 7505935143439181025, + "Slots": [ + { + "id": { + "m_id": "{67E7AF1F-F028-4460-819A-3FA6B9135F7B}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "toolTip": "Input signal", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{C6EB1567-2D26-4BC6-AC09-CB25183B1E0F}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Reset", + "toolTip": "Reset signal", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{922907EE-9B7C-49CF-80A2-0A9FF5CDCB8C}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "toolTip": "Output signal", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{BD2D869A-A1F9-4CA3-AFF7-D8134A36D56A}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "On Reset", + "toolTip": "Triggered when Reset", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ] + } + } + }, + { + "Id": { + "id": 31237116387228 + }, + "Name": "SC-Node(OrderedSequencer)", + "Components": { + "Component_[8845125901058188451]": { + "$type": "OrderedSequencer", + "Id": 8845125901058188451, + "Slots": [ + { + "id": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{E69D1672-B443-49D2-8228-9D45F4A4398C}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 0", + "toolTip": "Output 0", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{58EB3964-B87B-4C73-AD7B-532E4BBAD9E2}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 1", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{E965D8DB-1EDF-4CB2-910E-6782A6DD8487}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 2", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{786CF87F-5153-44CE-A995-59CAAA0ED040}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 3", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{60D9FB27-2CAC-412A-8B3B-2E4B4574FD56}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 4", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{CBD2A485-2FF1-405A-9940-E4268ED8EEC0}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 5", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{F71323EC-F182-465B-8671-C83BB07B227A}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 6", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{AFCB2F92-E497-4BBD-92B4-1E731246B834}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 7", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{973D553A-3231-461F-B46A-9A868846BE0B}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 8", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{251CF78F-0D57-4345-B80F-3947FBD30FCE}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 9", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{99CB5CB4-FF23-4C2B-9FBB-94D7752FCE80}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 10", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{2A550F0A-D5D2-497A-AFF2-EBB330F29A4D}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 11", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{FBD12806-F42E-4896-A44D-E0E34C8977BF}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 12", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{6552891F-4D35-4B2F-AE7D-B6CCE77B6966}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 13", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{119F60AD-CF01-4F6D-AC9D-F76ED05EF408}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 14", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{7ECBB1CC-6F3A-442E-B063-2B143F26ECF0}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 15", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{58AAA7C2-843B-4B0A-8451-B509284BFC6B}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 16", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{C6B341C5-8F1F-4F38-96B1-07873BC03493}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 17", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ] + } + } + }, + { + "Id": { + "id": 31245706321820 + }, + "Name": "SC-Node(OrderedSequencer)", + "Components": { + "Component_[8845125901058188451]": { + "$type": "OrderedSequencer", + "Id": 8845125901058188451, + "Slots": [ + { + "id": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{E69D1672-B443-49D2-8228-9D45F4A4398C}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 0", + "toolTip": "Output 0", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{58EB3964-B87B-4C73-AD7B-532E4BBAD9E2}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 1", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{E965D8DB-1EDF-4CB2-910E-6782A6DD8487}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 2", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{786CF87F-5153-44CE-A995-59CAAA0ED040}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 3", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{60D9FB27-2CAC-412A-8B3B-2E4B4574FD56}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 4", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{CBD2A485-2FF1-405A-9940-E4268ED8EEC0}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 5", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{F71323EC-F182-465B-8671-C83BB07B227A}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 6", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{AFCB2F92-E497-4BBD-92B4-1E731246B834}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 7", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{973D553A-3231-461F-B46A-9A868846BE0B}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 8", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{251CF78F-0D57-4345-B80F-3947FBD30FCE}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 9", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{99CB5CB4-FF23-4C2B-9FBB-94D7752FCE80}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 10", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{2A550F0A-D5D2-497A-AFF2-EBB330F29A4D}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 11", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{FBD12806-F42E-4896-A44D-E0E34C8977BF}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 12", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{6552891F-4D35-4B2F-AE7D-B6CCE77B6966}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 13", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{119F60AD-CF01-4F6D-AC9D-F76ED05EF408}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 14", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{7ECBB1CC-6F3A-442E-B063-2B143F26ECF0}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 15", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{8BC13109-B3A3-4931-8E54-0DFC6383A274}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 16", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{3ED549E3-C72F-4EC5-8CD6-2B8A36A8E503}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out 17", + "DisplayGroup": { + "Value": 1020632324 + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ] + } + } + } + ], + "m_connections": [ + { + "Id": { + "id": 31318720765852 + }, + "Name": "srcEndpoint=(On Graph Start: Out), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[11968085700009090183]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 11968085700009090183, + "sourceEndpoint": { + "nodeId": { + "id": 31262886191004 + }, + "slotId": { + "m_id": "{E1417C09-E03B-4EA9-B9EC-67F8EC862B69}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31323015733148 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 0), destEndpoint=(For Each: In)", + "Components": { + "Component_[14051685564799899080]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 14051685564799899080, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{E69D1672-B443-49D2-8228-9D45F4A4398C}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31284361027484 + }, + "slotId": { + "m_id": "{A9E6B254-446D-492F-B2CF-929CDC04ABFF}" + } + } + } + } + }, + { + "Id": { + "id": 31327310700444 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 1), destEndpoint=(And: In)", + "Components": { + "Component_[5835922357284603096]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 5835922357284603096, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{58EB3964-B87B-4C73-AD7B-532E4BBAD9E2}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31228526452636 + }, + "slotId": { + "m_id": "{FAFAB55A-69F1-495D-82FC-7D36F7842DA9}" + } + } + } + } + }, + { + "Id": { + "id": 31331605667740 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 2), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[11349254841334591199]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 11349254841334591199, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{E965D8DB-1EDF-4CB2-910E-6782A6DD8487}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31275771092892 + }, + "slotId": { + "m_id": "{E4E25252-6AE7-4A4F-A175-50B9C6CD5DAE}" + } + } + } + } + }, + { + "Id": { + "id": 31335900635036 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 3), destEndpoint=(Is Null: In)", + "Components": { + "Component_[11724175899893098283]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 11724175899893098283, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{786CF87F-5153-44CE-A995-59CAAA0ED040}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31250001289116 + }, + "slotId": { + "m_id": "{2215C7E1-3EA3-4487-BA74-0A5628424CEA}" + } + } + } + } + }, + { + "Id": { + "id": 31340195602332 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 4), destEndpoint=(Or: In)", + "Components": { + "Component_[15445788857322783773]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 15445788857322783773, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{60D9FB27-2CAC-412A-8B3B-2E4B4574FD56}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31211346583452 + }, + "slotId": { + "m_id": "{C5E1B10E-FDFA-45F8-B6AF-BD9C672986FC}" + } + } + } + } + }, + { + "Id": { + "id": 31344490569628 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 5), destEndpoint=(Switch: In)", + "Components": { + "Component_[9877129165904558121]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 9877129165904558121, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{CBD2A485-2FF1-405A-9940-E4268ED8EEC0}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31258591223708 + }, + "slotId": { + "m_id": "{52F5A723-7921-4644-AB19-5DCB94B51494}" + } + } + } + } + }, + { + "Id": { + "id": 31348785536924 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 6), destEndpoint=(Not: In)", + "Components": { + "Component_[3561874480108576621]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 3561874480108576621, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{F71323EC-F182-465B-8671-C83BB07B227A}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31280066060188 + }, + "slotId": { + "m_id": "{120C810C-F84A-445F-82E1-FD06CD3AD9B7}" + } + } + } + } + }, + { + "Id": { + "id": 31353080504220 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 10), destEndpoint=(Add (+): In)", + "Components": { + "Component_[15711843380878163015]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 15711843380878163015, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{99CB5CB4-FF23-4C2B-9FBB-94D7752FCE80}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31310130831260 + }, + "slotId": { + "m_id": "{792F23CB-7356-4DCA-A186-69F2A0BBC063}" + } + } + } + } + }, + { + "Id": { + "id": 31357375471516 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 11), destEndpoint=(Math Expression: In)", + "Components": { + "Component_[10938060626218586852]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 10938060626218586852, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{2A550F0A-D5D2-497A-AFF2-EBB330F29A4D}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31241411354524 + }, + "slotId": { + "m_id": "{6F8E4206-72C6-40E6-8AC1-205AA2C04804}" + } + } + } + } + }, + { + "Id": { + "id": 31361670438812 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 12), destEndpoint=(IsZero: In)", + "Components": { + "Component_[7885809696299116611]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 7885809696299116611, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{FBD12806-F42E-4896-A44D-E0E34C8977BF}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31207051616156 + }, + "slotId": { + "m_id": "{048F79E8-478C-46A1-AF3F-0B578022240D}" + } + } + } + } + }, + { + "Id": { + "id": 31365965406108 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 13), destEndpoint=(Extract Properties: In)", + "Components": { + "Component_[4352059250555138202]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 4352059250555138202, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{6552891F-4D35-4B2F-AE7D-B6CCE77B6966}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31232821419932 + }, + "slotId": { + "m_id": "{EADFB5B4-ADE0-4935-A102-99F8EC0C711A}" + } + } + } + } + }, + { + "Id": { + "id": 31370260373404 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 14), destEndpoint=(Add (+): In)", + "Components": { + "Component_[4078469470815155794]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 4078469470815155794, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{119F60AD-CF01-4F6D-AC9D-F76ED05EF408}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31267181158300 + }, + "slotId": { + "m_id": "{CDEA58A6-1B59-4476-B26E-0D5A828F35C7}" + } + } + } + } + }, + { + "Id": { + "id": 31374555340700 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 15), destEndpoint=(Multiply (*): In)", + "Components": { + "Component_[17350434400301914971]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 17350434400301914971, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{7ECBB1CC-6F3A-442E-B063-2B143F26ECF0}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31215641550748 + }, + "slotId": { + "m_id": "{2D2F75D9-A1FB-4D70-86EF-8518E119DB07}" + } + } + } + } + }, + { + "Id": { + "id": 31378850307996 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 14), destEndpoint=(Add (+): In)", + "Components": { + "Component_[2124314791740833510]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 2124314791740833510, + "sourceEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{119F60AD-CF01-4F6D-AC9D-F76ED05EF408}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31305835863964 + }, + "slotId": { + "m_id": "{CDEA58A6-1B59-4476-B26E-0D5A828F35C7}" + } + } + } + } + }, + { + "Id": { + "id": 31383145275292 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 0), destEndpoint=(For Each: In)", + "Components": { + "Component_[14483355239609043570]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 14483355239609043570, + "sourceEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{E69D1672-B443-49D2-8228-9D45F4A4398C}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31224231485340 + }, + "slotId": { + "m_id": "{A9E6B254-446D-492F-B2CF-929CDC04ABFF}" + } + } + } + } + }, + { + "Id": { + "id": 31387440242588 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 2), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[4849582226410181010]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 4849582226410181010, + "sourceEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{E965D8DB-1EDF-4CB2-910E-6782A6DD8487}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31292950962076 + }, + "slotId": { + "m_id": "{E4E25252-6AE7-4A4F-A175-50B9C6CD5DAE}" + } + } + } + } + }, + { + "Id": { + "id": 31391735209884 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 13), destEndpoint=(Extract Properties: In)", + "Components": { + "Component_[16660742051689195679]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 16660742051689195679, + "sourceEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{6552891F-4D35-4B2F-AE7D-B6CCE77B6966}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31301540896668 + }, + "slotId": { + "m_id": "{EADFB5B4-ADE0-4935-A102-99F8EC0C711A}" + } + } + } + } + }, + { + "Id": { + "id": 31396030177180 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 11), destEndpoint=(Math Expression: In)", + "Components": { + "Component_[761902755754097552]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 761902755754097552, + "sourceEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{2A550F0A-D5D2-497A-AFF2-EBB330F29A4D}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31219936518044 + }, + "slotId": { + "m_id": "{6F8E4206-72C6-40E6-8AC1-205AA2C04804}" + } + } + } + } + }, + { + "Id": { + "id": 31400325144476 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 3), destEndpoint=(Is Null: In)", + "Components": { + "Component_[11478294740641420074]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 11478294740641420074, + "sourceEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{786CF87F-5153-44CE-A995-59CAAA0ED040}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31254296256412 + }, + "slotId": { + "m_id": "{2215C7E1-3EA3-4487-BA74-0A5628424CEA}" + } + } + } + } + }, + { + "Id": { + "id": 31404620111772 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 12), destEndpoint=(IsZero: In)", + "Components": { + "Component_[6172045094798670980]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 6172045094798670980, + "sourceEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{FBD12806-F42E-4896-A44D-E0E34C8977BF}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31198461681564 + }, + "slotId": { + "m_id": "{048F79E8-478C-46A1-AF3F-0B578022240D}" + } + } + } + } + }, + { + "Id": { + "id": 31408915079068 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 5), destEndpoint=(Switch: In)", + "Components": { + "Component_[10417773454009289241]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 10417773454009289241, + "sourceEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{CBD2A485-2FF1-405A-9940-E4268ED8EEC0}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31297245929372 + }, + "slotId": { + "m_id": "{52F5A723-7921-4644-AB19-5DCB94B51494}" + } + } + } + } + }, + { + "Id": { + "id": 31413210046364 + }, + "Name": "srcEndpoint=(For Each: Each), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[4836133045899771729]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 4836133045899771729, + "sourceEndpoint": { + "nodeId": { + "id": 31284361027484 + }, + "slotId": { + "m_id": "{8A4B7BED-4921-4C08-B35B-56D129A630E9}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31417505013660 + }, + "Name": "srcEndpoint=(For Each: Finished), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[6237489035629954338]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 6237489035629954338, + "sourceEndpoint": { + "nodeId": { + "id": 31284361027484 + }, + "slotId": { + "m_id": "{0DEFFE74-9D9C-4E18-AFA2-12B1657F7D1D}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31421799980956 + }, + "Name": "srcEndpoint=(And: True), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[4927775769680604779]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 4927775769680604779, + "sourceEndpoint": { + "nodeId": { + "id": 31228526452636 + }, + "slotId": { + "m_id": "{5BDD309F-0047-43E9-A911-475E47A036EC}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31426094948252 + }, + "Name": "srcEndpoint=(And: False), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[12544845157105431583]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 12544845157105431583, + "sourceEndpoint": { + "nodeId": { + "id": 31228526452636 + }, + "slotId": { + "m_id": "{7438C80C-9044-442D-8C71-827AB7DC3F0B}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31430389915548 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 0), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[13496863929205474320]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 13496863929205474320, + "sourceEndpoint": { + "nodeId": { + "id": 31275771092892 + }, + "slotId": { + "m_id": "{425822FB-C61E-4493-98C3-3A05418BD029}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31434684882844 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 1), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[12523441803443343729]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 12523441803443343729, + "sourceEndpoint": { + "nodeId": { + "id": 31275771092892 + }, + "slotId": { + "m_id": "{8FDC2E61-37AE-4E0F-82FE-7A4D9696123C}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31438979850140 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 2), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[4247029149608940629]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 4247029149608940629, + "sourceEndpoint": { + "nodeId": { + "id": 31275771092892 + }, + "slotId": { + "m_id": "{1000AB91-55FB-4615-B0EE-306816CFE895}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31443274817436 + }, + "Name": "srcEndpoint=(Is Null: True), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[2748837971044280491]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 2748837971044280491, + "sourceEndpoint": { + "nodeId": { + "id": 31250001289116 + }, + "slotId": { + "m_id": "{AA375295-D5E8-473C-A073-08A2853E750E}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31447569784732 + }, + "Name": "srcEndpoint=(Is Null: False), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[2379529025270594940]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 2379529025270594940, + "sourceEndpoint": { + "nodeId": { + "id": 31250001289116 + }, + "slotId": { + "m_id": "{342C520C-CA78-4261-91C9-629978B79BA3}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31451864752028 + }, + "Name": "srcEndpoint=(Or: True), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[4662929964571753389]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 4662929964571753389, + "sourceEndpoint": { + "nodeId": { + "id": 31211346583452 + }, + "slotId": { + "m_id": "{7FED3519-4670-4B3D-B81F-44393C2D7787}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31456159719324 + }, + "Name": "srcEndpoint=(Or: False), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[10410455197671109469]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 10410455197671109469, + "sourceEndpoint": { + "nodeId": { + "id": 31211346583452 + }, + "slotId": { + "m_id": "{E2D1192C-902D-4C82-9DE4-BBC20B31A54B}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31460454686620 + }, + "Name": "srcEndpoint=(Switch: Out 0), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[10099436517331803126]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 10099436517331803126, + "sourceEndpoint": { + "nodeId": { + "id": 31258591223708 + }, + "slotId": { + "m_id": "{800E14B1-C788-4C7F-B4D4-76121DE547C9}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31464749653916 + }, + "Name": "srcEndpoint=(Switch: Out 1), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[11535766430360773447]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 11535766430360773447, + "sourceEndpoint": { + "nodeId": { + "id": 31258591223708 + }, + "slotId": { + "m_id": "{9828EDD9-2689-424C-9708-304D720B1348}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31469044621212 + }, + "Name": "srcEndpoint=(Switch: Out 2), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[3612784336888322174]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 3612784336888322174, + "sourceEndpoint": { + "nodeId": { + "id": 31258591223708 + }, + "slotId": { + "m_id": "{4A1E25C6-E63B-4BC0-BBDE-533CA69C91C5}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31473339588508 + }, + "Name": "srcEndpoint=(Not: True), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[4611790098876313333]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 4611790098876313333, + "sourceEndpoint": { + "nodeId": { + "id": 31280066060188 + }, + "slotId": { + "m_id": "{6E82DFA3-70F4-419F-9971-AD243313E8D9}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31477634555804 + }, + "Name": "srcEndpoint=(Not: False), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[3740076693359624525]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 3740076693359624525, + "sourceEndpoint": { + "nodeId": { + "id": 31280066060188 + }, + "slotId": { + "m_id": "{5C53A008-E808-4D00-A4EC-AC6A0EFDAFB9}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31481929523100 + }, + "Name": "srcEndpoint=(Add (+): Out), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[3850851395875718062]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 3850851395875718062, + "sourceEndpoint": { + "nodeId": { + "id": 31310130831260 + }, + "slotId": { + "m_id": "{775F77BE-A9EC-455D-B378-15D16A0BEA4F}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31486224490396 + }, + "Name": "srcEndpoint=(Math Expression: Out), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[3602036390975897875]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 3602036390975897875, + "sourceEndpoint": { + "nodeId": { + "id": 31241411354524 + }, + "slotId": { + "m_id": "{5E038FBC-9A4B-489C-AB3E-26F103B7102E}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31490519457692 + }, + "Name": "srcEndpoint=(IsZero: Out), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[17614443468886602012]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 17614443468886602012, + "sourceEndpoint": { + "nodeId": { + "id": 31207051616156 + }, + "slotId": { + "m_id": "{2CD5D252-4872-41A2-9C9E-4B81AD9877B7}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31494814424988 + }, + "Name": "srcEndpoint=(Extract Properties: Out), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[4800366554066352627]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 4800366554066352627, + "sourceEndpoint": { + "nodeId": { + "id": 31232821419932 + }, + "slotId": { + "m_id": "{AC7594DE-A7F0-4519-9E07-0350AEEB25E0}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31499109392284 + }, + "Name": "srcEndpoint=(Add (+): Out), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[15231344167676276156]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 15231344167676276156, + "sourceEndpoint": { + "nodeId": { + "id": 31267181158300 + }, + "slotId": { + "m_id": "{5E66E5C1-AE26-4919-BFD7-66EC6DE9B7BB}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31503404359580 + }, + "Name": "srcEndpoint=(Multiply (*): Out), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[13866431563723516549]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 13866431563723516549, + "sourceEndpoint": { + "nodeId": { + "id": 31215641550748 + }, + "slotId": { + "m_id": "{7A580403-C074-4DAD-A65E-BF928EF19DC8}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31507699326876 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 16), destEndpoint=(TickBus Handler: Connect)", + "Components": { + "Component_[4065717098596350153]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 4065717098596350153, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{8BC13109-B3A3-4931-8E54-0DFC6383A274}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31314425798556 + }, + "slotId": { + "m_id": "{C189EE08-DC89-4AF2-88BC-B564F84F28A6}" + } + } + } + } + }, + { + "Id": { + "id": 31511994294172 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 17), destEndpoint=(TickBus Handler: Disconnect)", + "Components": { + "Component_[451545062627990235]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 451545062627990235, + "sourceEndpoint": { + "nodeId": { + "id": 31245706321820 + }, + "slotId": { + "m_id": "{3ED549E3-C72F-4EC5-8CD6-2B8A36A8E503}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31314425798556 + }, + "slotId": { + "m_id": "{10BB0A14-C955-464E-8745-7FBC88E24049}" + } + } + } + } + }, + { + "Id": { + "id": 31516289261468 + }, + "Name": "srcEndpoint=(TickBus Handler: OnConnected), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[16416933153170812903]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 16416933153170812903, + "sourceEndpoint": { + "nodeId": { + "id": 31314425798556 + }, + "slotId": { + "m_id": "{A7026536-ED61-48CA-8329-601DACF8A178}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31520584228764 + }, + "Name": "srcEndpoint=(TickBus Handler: OnDisconnected), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[16427931620012509498]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 16427931620012509498, + "sourceEndpoint": { + "nodeId": { + "id": 31314425798556 + }, + "slotId": { + "m_id": "{95E02974-9E1C-407C-94DA-087C5B4EC55C}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31524879196060 + }, + "Name": "srcEndpoint=(TickBus Handler: ExecutionSlot:OnTick), destEndpoint=(Ordered Sequencer: In)", + "Components": { + "Component_[12537264777285639380]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 12537264777285639380, + "sourceEndpoint": { + "nodeId": { + "id": 31314425798556 + }, + "slotId": { + "m_id": "{99FA955C-7E6E-4539-85B0-16CBFBB1F0E3}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{06EAF2BD-2430-46D2-A19F-6E785B06A1B2}" + } + } + } + } + }, + { + "Id": { + "id": 31529174163356 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 16), destEndpoint=(TickBus Handler: Connect)", + "Components": { + "Component_[4140744289594349719]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 4140744289594349719, + "sourceEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{58AAA7C2-843B-4B0A-8451-B509284BFC6B}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31288655994780 + }, + "slotId": { + "m_id": "{C189EE08-DC89-4AF2-88BC-B564F84F28A6}" + } + } + } + } + }, + { + "Id": { + "id": 31533469130652 + }, + "Name": "srcEndpoint=(Ordered Sequencer: Out 17), destEndpoint=(TickBus Handler: Disconnect)", + "Components": { + "Component_[6223827301708831635]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 6223827301708831635, + "sourceEndpoint": { + "nodeId": { + "id": 31237116387228 + }, + "slotId": { + "m_id": "{C6B341C5-8F1F-4F38-96B1-07873BC03493}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31288655994780 + }, + "slotId": { + "m_id": "{10BB0A14-C955-464E-8745-7FBC88E24049}" + } + } + } + } + }, + { + "Id": { + "id": 31537764097948 + }, + "Name": "srcEndpoint=(TickBus Handler: OnConnected), destEndpoint=(Once: In)", + "Components": { + "Component_[16328568633919858732]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 16328568633919858732, + "sourceEndpoint": { + "nodeId": { + "id": 31288655994780 + }, + "slotId": { + "m_id": "{A7026536-ED61-48CA-8329-601DACF8A178}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31271476125596 + }, + "slotId": { + "m_id": "{67E7AF1F-F028-4460-819A-3FA6B9135F7B}" + } + } + } + } + }, + { + "Id": { + "id": 31542059065244 + }, + "Name": "srcEndpoint=(TickBus Handler: OnDisconnected), destEndpoint=(Once: In)", + "Components": { + "Component_[5193640857027050517]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 5193640857027050517, + "sourceEndpoint": { + "nodeId": { + "id": 31288655994780 + }, + "slotId": { + "m_id": "{95E02974-9E1C-407C-94DA-087C5B4EC55C}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31271476125596 + }, + "slotId": { + "m_id": "{67E7AF1F-F028-4460-819A-3FA6B9135F7B}" + } + } + } + } + }, + { + "Id": { + "id": 31546354032540 + }, + "Name": "srcEndpoint=(Once: Out), destEndpoint=(Mark Complete: In)", + "Components": { + "Component_[5213414421933364896]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 5213414421933364896, + "sourceEndpoint": { + "nodeId": { + "id": 31271476125596 + }, + "slotId": { + "m_id": "{922907EE-9B7C-49CF-80A2-0A9FF5CDCB8C}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 31202756648860 + }, + "slotId": { + "m_id": "{79C2ABDD-F20A-4042-BF5C-8C09DC9332C7}" + } + } + } + } + } + ] + }, + "m_assetType": "{3E2AC8CD-713F-453E-967F-29517F331784}", + "versionData": { + "_grammarVersion": 1, + "_runtimeVersion": 1 + }, + "m_variableCounter": 2, + "GraphCanvasData": [ + { + "Key": { + "id": 31194166714268 + }, + "Value": { + "ComponentData": { + "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { + "$type": "SceneComponentSaveData", + "ViewParams": { + "Scale": 0.2579344, + "AnchorX": -2508.39013671875, + "AnchorY": 1461.6121826171876 + } + } + } + } + }, + { + "Key": { + "id": 31198461681564 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MathNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 2040.0, + 3260.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{810D332E-6C30-437C-9BCA-DF663A237034}" + } + } + } + }, + { + "Key": { + "id": 31202756648860 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "TestingNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 2640.0, + 4320.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{90DEA5B5-B658-4163-964E-91DB8D0D0F91}" + } + } + } + }, + { + "Key": { + "id": 31207051616156 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MathNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 300.0, + 3320.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{AE920511-0E26-4EEF-ADAD-982BEA59BA94}" + } + } + } + }, + { + "Key": { + "id": 31211346583452 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "LogicNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 300.0, + 1460.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{0C7217EF-A630-46C6-A1C8-D4202697B60E}" + } + } + } + }, + { + "Key": { + "id": 31215641550748 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MathNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 300.0, + 4020.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{E6E72236-4B7B-47AD-ABF9-08E0D1691F72}" + } + } + } + }, + { + "Key": { + "id": 31219936518044 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MathNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 2040.0, + 2980.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{C2544B7B-3AA0-4731-81CF-DA88A5E210EC}" + } + } + } + }, + { + "Key": { + "id": 31224231485340 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "DefaultNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 2040.0, + 340.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{EDABFAD6-F9B5-4490-8EC6-165291FF3588}" + } + } + } + }, + { + "Key": { + "id": 31228526452636 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "LogicNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 300.0, + 700.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{F60EF486-6C8B-4943-B3F4-600F8148DE78}" + } + } + } + }, + { + "Key": { + "id": 31232821419932 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "DefaultNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 300.0, + 3580.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{BB3891CA-9936-4F18-8552-8EB7F2D02206}" + } + } + } + }, + { + "Key": { + "id": 31237116387228 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "LogicNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 1200.0, + 2660.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{05F42C5B-4887-4800-8422-D5E5C1361E5F}" + } + } + } + }, + { + "Key": { + "id": 31241411354524 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MathNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 300.0, + 3040.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{32F17925-C934-4CC7-918A-3026954CABD6}" + } + } + } + }, + { + "Key": { + "id": 31245706321820 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "LogicNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + -480.0, + 2320.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{AC1D06EF-C967-4F35-95BD-8D822F4A2CE7}" + } + } + } + }, + { + "Key": { + "id": 31250001289116 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "LogicNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 300.0, + 1240.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{63DF2982-B424-4D30-BF05-9DA9DD5664EC}" + } + } + } + }, + { + "Key": { + "id": 31254296256412 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "LogicNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 2040.0, + 1180.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{9952383B-0B57-43BB-B59C-501C3F2CDE30}" + } + } + } + }, + { + "Key": { + "id": 31258591223708 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "LogicNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 300.0, + 1740.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{E6746414-838B-4AF7-AC50-EBEB0D2C06C2}" + } + } + } + }, + { + "Key": { + "id": 31262886191004 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "TimeNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + -900.0, + 2400.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{D3649B82-A382-47A3-8168-BC7BA0377449}" + } + } + } + }, + { + "Key": { + "id": 31267181158300 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MathNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 300.0, + 3780.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{D305A28C-D38B-4A34-BEA1-7E7AA92B853A}" + } + } + } + }, + { + "Key": { + "id": 31271476125596 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "LogicNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 2420.0, + 4320.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{0CC6067B-5802-4AB3-9FF4-C1DF2C283DBB}" + } + } + } + }, + { + "Key": { + "id": 31275771092892 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "LogicNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 300.0, + 960.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{55307FED-8C26-4EC5-98B5-F080C79E784D}" + } + } + } + }, + { + "Key": { + "id": 31280066060188 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "LogicNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 300.0, + 2000.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{5E0AD895-3D93-41F0-86D0-0A9DC4305145}" + } + } + } + }, + { + "Key": { + "id": 31284361027484 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "DefaultNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 300.0, + 400.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{F37ED565-54EF-4A7C-9FB4-ABA1A71A4A36}" + } + } + } + }, + { + "Key": { + "id": 31288655994780 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 2080.0, + 4340.0 + ] + }, + "{9E81C95F-89C0-4476-8E82-63CCC4E52E04}": { + "$type": "EBusHandlerNodeDescriptorSaveData", + "DisplayConnections": true, + "EventIds": [ + { + "Value": 1502188240 + } + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{58CBCF78-2B53-43EB-85BE-309EDB183924}" + } + } + } + }, + { + "Key": { + "id": 31292950962076 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "LogicNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 2040.0, + 900.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{12114A6E-22BE-4A7A-8211-7A1282B900EC}" + } + } + } + }, + { + "Key": { + "id": 31297245929372 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "LogicNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 2040.0, + 1680.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{E6DB84DC-F8BE-4C8E-BE0D-A0B96652B2C0}" + } + } + } + }, + { + "Key": { + "id": 31301540896668 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "DefaultNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 2040.0, + 3520.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{EFAA6315-4594-4432-A71C-C50AC21A3B9C}" + } + } + } + }, + { + "Key": { + "id": 31305835863964 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MathNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 2040.0, + 3720.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{ABCB6F4F-5683-4958-B544-E543B5591129}" + } + } + } + }, + { + "Key": { + "id": 31310130831260 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MathNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 300.0, + 2820.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{0F538778-0124-48B9-B8D8-DB50D3620953}" + } + } + } + }, + { + "Key": { + "id": 31314425798556 + }, + "Value": { + "ComponentData": { + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 340.0, + 4320.0 + ] + }, + "{9E81C95F-89C0-4476-8E82-63CCC4E52E04}": { + "$type": "EBusHandlerNodeDescriptorSaveData", + "DisplayConnections": true, + "EventIds": [ + { + "Value": 1502188240 + } + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{3B6A148E-D130-4461-BA4D-EF0C91E7B729}" + } + } + } + } + ], + "StatisticsHelper": { + "InstanceCounter": [ + { + "Key": 1244476766431948410, + "Value": 3 + }, + { + "Key": 1675714605584791791, + "Value": 1 + }, + { + "Key": 1899400123866786017, + "Value": 2 + }, + { + "Key": 4199610336680704683, + "Value": 1 + }, + { + "Key": 4271004856738215795, + "Value": 4 + }, + { + "Key": 5842117451819972883, + "Value": 2 + }, + { + "Key": 7224637793062157918, + "Value": 2 + }, + { + "Key": 7441743364271812807, + "Value": 1 + }, + { + "Key": 10181512461692697578, + "Value": 2 + }, + { + "Key": 10204019744198319120, + "Value": 1 + }, + { + "Key": 11764063517792044591, + "Value": 2 + }, + { + "Key": 12702286953450386850, + "Value": 1 + }, + { + "Key": 14285852892804039565, + "Value": 2 + }, + { + "Key": 14502416192304263066, + "Value": 2 + }, + { + "Key": 14935618182048638376, + "Value": 1 + }, + { + "Key": 18321430858034101497, + "Value": 1 + } + ] + } + }, + "Component_[4373975411826598062]": { + "$type": "EditorGraphVariableManagerComponent", + "Id": 4373975411826598062, + "m_variableData": { + "m_nameVariableMap": [ + { + "Key": { + "m_id": "{0BA96D88-BF13-4810-B01C-CA513AF84440}" + }, + "Value": { + "Datum": { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 3 + }, + "isNullPointer": false, + "$type": "double", + "value": 0.0, + "label": "Number" + }, + "VariableId": { + "m_id": "{0BA96D88-BF13-4810-B01C-CA513AF84440}" + }, + "VariableName": "Variable 2" + } + }, + { + "Key": { + "m_id": "{39DB51CB-AA6E-4B3A-93EC-D622C44B3F4B}" + }, + "Value": { + "Datum": { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 4, + "m_azType": "{B505B05D-57BF-5A45-9113-B3A2784E2CD7}" + }, + "isNullPointer": false, + "$type": "{B505B05D-57BF-5A45-9113-B3A2784E2CD7} AZStd::vector", + "value": [ + true, + true + ], + "label": "Array" + }, + "VariableId": { + "m_id": "{39DB51CB-AA6E-4B3A-93EC-D622C44B3F4B}" + }, + "VariableName": "Array_of_Bools" + } + } + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/Gems/Terrain/Assets/Shaders/Terrain/Terrain.azsl b/Gems/Terrain/Assets/Shaders/Terrain/Terrain.azsl new file mode 100644 index 0000000000..57d0909ae4 --- /dev/null +++ b/Gems/Terrain/Assets/Shaders/Terrain/Terrain.azsl @@ -0,0 +1,125 @@ +/* + * 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 +#include + + +struct VertexInput +{ + float2 Position : POSITION; + float2 UV : UV; +}; + +struct VertexOutput +{ + float4 Position : SV_Position; + float3 Normal : NORMAL; + float2 UV : UV; +}; + +ShaderResourceGroup ObjectSrg : SRG_PerObject +{ + Texture2D HeightmapImage; + + Sampler LinearSampler + { + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Linear; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; + + row_major float3x4 m_modelToWorld; + float m_heightScale; + float2 m_uvMin; + float2 m_uvMax; + float2 m_uvStep; +} + +float4x4 GetObject_WorldMatrix() +{ + float4x4 modelToWorld = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorld[0] = ObjectSrg::m_modelToWorld[0]; + modelToWorld[1] = ObjectSrg::m_modelToWorld[1]; + modelToWorld[2] = ObjectSrg::m_modelToWorld[2]; + return modelToWorld; +} + +float GetHeight(float2 origUv) +{ + float2 uv = clamp(origUv, 0.0f, 1.0f); + return ObjectSrg::m_heightScale * (ObjectSrg::HeightmapImage.SampleLevel(ObjectSrg::LinearSampler, uv, 0).r - 0.5f); +} + +VertexOutput MainVS(in VertexInput input) +{ + VertexOutput output; + + // Clamp the UVs *after* lerping to ensure that everything aligns properly right to the edge. + // We use out-of-bounds UV values to denote vertices that need to be removed. + float2 origUv = lerp(ObjectSrg::m_uvMin, ObjectSrg::m_uvMax, input.UV); + float2 uv = clamp(origUv, 0.0f, 1.0f); + + // Loop up the height and calculate our final position. + float height = GetHeight(uv); + float3 worldPosition = mul(GetObject_WorldMatrix(), float4(input.Position, height, 1.0f)).xyz; + output.Position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0f)); + + // Remove all vertices outside our bounds by turning them into NaN positions. + output.Position = output.Position / ((origUv.x >= 0.0f && origUv.x < 1.0f && origUv.y >= 0.0f && origUv.y < 1.0f) ? 1.0f : 0.0f); + + // Calculate normal + float2 gridSize = {1.0f, 1.0f}; + float up = GetHeight(uv + ObjectSrg::m_uvStep * float2(-1.0f, 0.0f)); + float right = GetHeight(uv + ObjectSrg::m_uvStep * float2( 0.0f, 1.0f)); + float down = GetHeight(uv + ObjectSrg::m_uvStep * float2( 1.0f, 0.0f)); + float left = GetHeight(uv + ObjectSrg::m_uvStep * float2( 0.0f, -1.0f)); + + float dydx = (right - left) * gridSize[0]; + float dydz = (down - up) * gridSize[1]; + + output.Normal = normalize(float3(dydx, 2.0f, dydz)); + + output.UV = uv; + return output; +} + +struct PixelOutput +{ + float4 m_color : SV_Target0; +}; + +PixelOutput MainPS(in VertexOutput input) +{ + PixelOutput output; + + // Hard-coded fake light direction + float3 lightDirection = normalize(float3(1.0, -1.0, 1.0)); + + // Fake light intensity ranges from 1.0 for normals directly facing the light to zero for those + // directly facing away. + float lightDot = dot(normalize(input.Normal), lightDirection); + float lightIntensity = lightDot * 0.5 + 0.5; + + // add a small amount of ambient and reduce direct light to keep in 0-1 range + lightIntensity = saturate(0.1 + lightIntensity * 0.9); + + // The lightIntensity should not affect alpha so only apply it to rgb. + //output.m_color.rgb = ((input.Normal + float3(1.0, 1.0, 1.0)) / 2.0); + output.m_color.rgb = float3(1.0, 1.0, 1.0) * lightIntensity; + output.m_color.a = 1.0f; + + return output; +} diff --git a/Gems/Terrain/Assets/Shaders/Terrain/Terrain.shader b/Gems/Terrain/Assets/Shaders/Terrain/Terrain.shader new file mode 100644 index 0000000000..f20f4201ae --- /dev/null +++ b/Gems/Terrain/Assets/Shaders/Terrain/Terrain.shader @@ -0,0 +1,37 @@ +{ + + "Source" : "Terrain", + + + "RasterState" : { "CullMode" : "None" }, + + "DepthStencilState" : { + "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } + }, + + "BlendState" : { + "Enable" : true, + "BlendSource" : "One", + "BlendAlphaSource" : "One", + "BlendDest" : "AlphaSourceInverse", + "BlendAlphaDest" : "AlphaSourceInverse", + "BlendAlphaOp" : "Add" + }, + + "DrawList" : "forward", + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp new file mode 100644 index 0000000000..d64f660d90 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp @@ -0,0 +1,231 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +namespace Terrain +{ + void TerrainHeightGradientListConfig::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("GradientEntities", &TerrainHeightGradientListConfig::m_gradientEntities) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class( + "Terrain Height Gradient List Component", "Provide height data for a region of the world") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + + ->DataElement(0, &TerrainHeightGradientListConfig::m_gradientEntities, "Gradient Entities", "Ordered list of gradients to use as height providers.") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, true) + ->Attribute(AZ::Edit::Attributes::RequiredService, AZ_CRC_CE("GradientService")) + ; + } + } + } + + void TerrainHeightGradientListComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainHeightProviderService")); + } + + void TerrainHeightGradientListComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainHeightProviderService")); + services.push_back(AZ_CRC_CE("GradientService")); + } + + void TerrainHeightGradientListComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainAreaService")); + services.push_back(AZ_CRC_CE("BoxShapeService")); + } + + void TerrainHeightGradientListComponent::Reflect(AZ::ReflectContext* context) + { + TerrainHeightGradientListConfig::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(0) + ->Field("Configuration", &TerrainHeightGradientListComponent::m_configuration) + ; + } + } + + TerrainHeightGradientListComponent::TerrainHeightGradientListComponent(const TerrainHeightGradientListConfig& configuration) + : m_configuration(configuration) + { + } + + void TerrainHeightGradientListComponent::Activate() + { + LmbrCentral::DependencyNotificationBus::Handler::BusConnect(GetEntityId()); + Terrain::TerrainAreaHeightRequestBus::Handler::BusConnect(GetEntityId()); + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); + + // Make sure we get update notifications whenever this entity or any dependent gradient entity changes in any way. + // We'll use that to notify the terrain system that the height information needs to be refreshed. + m_dependencyMonitor.Reset(); + m_dependencyMonitor.ConnectOwner(GetEntityId()); + m_dependencyMonitor.ConnectDependency(GetEntityId()); + + for (auto& entityId : m_configuration.m_gradientEntities) + { + if (entityId != GetEntityId()) + { + m_dependencyMonitor.ConnectDependency(entityId); + } + } + + // Cache any height data needed and notify that the area has changed. + OnCompositionChanged(); + } + + void TerrainHeightGradientListComponent::Deactivate() + { + m_dependencyMonitor.Reset(); + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); + Terrain::TerrainAreaHeightRequestBus::Handler::BusDisconnect(); + LmbrCentral::DependencyNotificationBus::Handler::BusDisconnect(); + + // Since this height data will no longer exist, notify the terrain system to refresh the area. + TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId()); + } + + bool TerrainHeightGradientListComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) + { + if (auto config = azrtti_cast(baseConfig)) + { + m_configuration = *config; + return true; + } + return false; + } + + bool TerrainHeightGradientListComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + { + if (auto config = azrtti_cast(outBaseConfig)) + { + *config = m_configuration; + return true; + } + return false; + } + + float TerrainHeightGradientListComponent::GetHeight(float x, float y) + { + float maxSample = 0.0f; + + GradientSignal::GradientSampleParams params(AZ::Vector3(x, y, 0.0f)); + + // Right now, when the list contains multiple entries, we will use the highest point from each gradient. + // This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value + // of 0 outside their data bounds if they're using bounded data. We should examine the possibility of extending the gradient API + // to provide actual bounds so that it's possible to detect if the gradient even 'exists' in an area, at which point we could just + // make this list a prioritized list from top to bottom for any points that overlap. + for (auto& gradientId : m_configuration.m_gradientEntities) + { + float sample = 0.0f; + GradientSignal::GradientRequestBus::EventResult(sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); + maxSample = AZ::GetMax(maxSample, sample); + } + + const float height = AZ::Lerp(m_cachedShapeBounds.GetMin().GetZ(), m_cachedShapeBounds.GetMax().GetZ(), maxSample); + + return AZ::GetClamp(height, m_cachedMinWorldHeight, m_cachedMaxWorldHeight); + } + + void TerrainHeightGradientListComponent::GetHeight( + const AZ::Vector3& inPosition, AZ::Vector3& outPosition, [[maybe_unused]] Sampler sampleFilter = Sampler::DEFAULT) + { + const float height = GetHeight(inPosition.GetX(), inPosition.GetY()); + outPosition.SetZ(height); + } + + void TerrainHeightGradientListComponent::GetNormal( + const AZ::Vector3& inPosition, AZ::Vector3& outNormal, [[maybe_unused]] Sampler sampleFilter = Sampler::DEFAULT) + { + const float x = inPosition.GetX(); + const float y = inPosition.GetY(); + + if ((x >= m_cachedShapeBounds.GetMin().GetX()) && (x <= m_cachedShapeBounds.GetMax().GetX()) && + (y >= m_cachedShapeBounds.GetMin().GetY()) && (y <= m_cachedShapeBounds.GetMax().GetY())) + { + AZ::Vector2 fRange = (m_cachedHeightQueryResolution / 2.0f) + AZ::Vector2(0.05f); + + AZ::Vector3 v1(x - fRange.GetX(), y - fRange.GetY(), GetHeight(x - fRange.GetX(), y - fRange.GetY())); + AZ::Vector3 v2(x - fRange.GetX(), y + fRange.GetY(), GetHeight(x - fRange.GetX(), y + fRange.GetY())); + AZ::Vector3 v3(x + fRange.GetX(), y - fRange.GetY(), GetHeight(x + fRange.GetX(), y - fRange.GetY())); + AZ::Vector3 v4(x + fRange.GetX(), y + fRange.GetY(), GetHeight(x + fRange.GetX(), y + fRange.GetY())); + outNormal = (v3 - v2).Cross(v4 - v1).GetNormalized(); + } + } + + + void TerrainHeightGradientListComponent::OnCompositionChanged() + { + RefreshMinMaxHeights(); + TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId()); + } + + void TerrainHeightGradientListComponent::RefreshMinMaxHeights() + { + // Get the height range of our height provider based on the shape component. + LmbrCentral::ShapeComponentRequestsBus::EventResult(m_cachedShapeBounds, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); + + // Get the height range of the entire world + m_cachedHeightQueryResolution = AZ::Vector2(1.0f); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + m_cachedHeightQueryResolution, &AzFramework::Terrain::TerrainDataRequestBus::Events::GetTerrainGridResolution); + + AZ::Aabb worldBounds = AZ::Aabb::CreateNull(); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + worldBounds, &AzFramework::Terrain::TerrainDataRequestBus::Events::GetTerrainAabb); + + // Save off the min/max heights so that we don't have to re-query them on every single height query. + m_cachedMinWorldHeight = worldBounds.GetMin().GetZ(); + m_cachedMaxWorldHeight = worldBounds.GetMax().GetZ(); + } + + void TerrainHeightGradientListComponent::OnTerrainDataChanged( + [[maybe_unused]] const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) + { + if (dataChangedMask & TerrainDataChangedMask::Settings) + { + // If the terrain system settings changed, it's possible that the world bounds have changed, which can affect our height data. + // Refresh the min/max heights and notify that the height data for this area needs to be refreshed. + OnCompositionChanged(); + } + } + +} diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h new file mode 100644 index 0000000000..7635680815 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h @@ -0,0 +1,101 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + + +namespace LmbrCentral +{ + template + class EditorWrappedComponentBase; +} + +namespace Terrain +{ + class TerrainHeightGradientListConfig + : public AZ::ComponentConfig + { + public: + AZ_CLASS_ALLOCATOR(TerrainHeightGradientListConfig, AZ::SystemAllocator, 0); + AZ_RTTI(TerrainHeightGradientListConfig, "{C5FD71A9-0722-4D4C-B605-EBEBF90C628F}", AZ::ComponentConfig); + static void Reflect(AZ::ReflectContext* context); + + AZStd::vector m_gradientEntities; + }; + + + class TerrainHeightGradientListComponent + : public AZ::Component + , private Terrain::TerrainAreaHeightRequestBus::Handler + , private LmbrCentral::DependencyNotificationBus::Handler + , private AzFramework::Terrain::TerrainDataNotificationBus::Handler + { + public: + template + friend class LmbrCentral::EditorWrappedComponentBase; + AZ_COMPONENT(TerrainHeightGradientListComponent, "{1BB3BA6C-6D4A-4636-B542-F23ECBA8F2AB}"); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void Reflect(AZ::ReflectContext* context); + + TerrainHeightGradientListComponent(const TerrainHeightGradientListConfig& configuration); + TerrainHeightGradientListComponent() = default; + ~TerrainHeightGradientListComponent() = default; + + void GetHeight(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, Sampler sampleFilter) override; + void GetNormal(const AZ::Vector3& inPosition, AZ::Vector3& outNormal, Sampler sampleFilter) override; + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override; + bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; + + ////////////////////////////////////////////////////////////////////////// + // LmbrCentral::DependencyNotificationBus + void OnCompositionChanged() override; + + ////////////////////////////////////////////////////////////////////////// + // AzFramework::Terrain::TerrainDataNotificationBus + void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override; + + private: + TerrainHeightGradientListConfig m_configuration; + + /////////////////////////////////////////// + void GetNormalSynchronous(float x, float y, AZ::Vector3& normal); + + void RefreshMinMaxHeights(); + float GetHeight(float x, float y); + + float m_cachedMinWorldHeight{ 0.0f }; + float m_cachedMaxWorldHeight{ 0.0f }; + AZ::Vector2 m_cachedHeightQueryResolution{ 1.0f, 1.0f }; + AZ::Aabb m_cachedShapeBounds; + + LmbrCentral::DependencyMonitor m_dependencyMonitor; + }; +} diff --git a/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.cpp new file mode 100644 index 0000000000..06372a3490 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.cpp @@ -0,0 +1,160 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Terrain +{ + void TerrainLayerSpawnerConfig::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("Layer", &TerrainLayerSpawnerConfig::m_layer) + ->Field("Priority", &TerrainLayerSpawnerConfig::m_priority) + ->Field("UseGroundPlane", &TerrainLayerSpawnerConfig::m_useGroundPlane) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class( + "Terrain Layer Spawner Component", "Provide terrain data for a region of the world") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &TerrainLayerSpawnerConfig::m_layer, "Layer Priority", "Defines a high level order that terrain spawners are applied") + ->Attribute(AZ::Edit::Attributes::EnumValues, &TerrainLayerSpawnerConfig::GetSelectableLayers) + ->DataElement(AZ::Edit::UIHandlers::Slider, &TerrainLayerSpawnerConfig::m_priority, "Sub Priority", "Defines order terrain spawners are applied within a layer. Larger numbers = higher priority") + ->Attribute(AZ::Edit::Attributes::Min, AreaConstants::s_priorityMin) + ->Attribute(AZ::Edit::Attributes::Max, AreaConstants::s_priorityMax) + ->Attribute(AZ::Edit::Attributes::SoftMin, AreaConstants::s_priorityMin) + ->Attribute(AZ::Edit::Attributes::SoftMax, AreaConstants::s_prioritySoftMax) + ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainLayerSpawnerConfig::m_useGroundPlane, "Use Ground Plane", "Determines whether or not to provide a default ground plane") + ; + } + } + } + + AZStd::vector> TerrainLayerSpawnerConfig::GetSelectableLayers() const + { + AZStd::vector> selectableLayers; + selectableLayers.push_back({ AreaConstants::s_backgroundLayer, AZStd::string("Background") }); + selectableLayers.push_back({ AreaConstants::s_foregroundLayer, AZStd::string("Foreground") }); + return selectableLayers; + } + + + void TerrainLayerSpawnerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC("TerrainAreaService")); + } + + void TerrainLayerSpawnerComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC("TerrainAreaService")); + } + + void TerrainLayerSpawnerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC("BoxShapeService")); + } + + void TerrainLayerSpawnerComponent::Reflect(AZ::ReflectContext* context) + { + TerrainLayerSpawnerConfig::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(0) + ->Field("Configuration", &TerrainLayerSpawnerComponent::m_configuration) + ; + } + } + + TerrainLayerSpawnerComponent::TerrainLayerSpawnerComponent(const TerrainLayerSpawnerConfig& configuration) + : m_configuration(configuration) + { + } + + void TerrainLayerSpawnerComponent::Activate() + { + AZ::TransformNotificationBus::Handler::BusConnect(GetEntityId()); + LmbrCentral::ShapeComponentNotificationsBus::Handler::BusConnect(GetEntityId()); + TerrainAreaRequestBus::Handler::BusConnect(GetEntityId()); + + TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RegisterArea, GetEntityId()); + } + + void TerrainLayerSpawnerComponent::Deactivate() + { + TerrainAreaRequestBus::Handler::BusDisconnect(); + TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::UnregisterArea, GetEntityId()); + + AZ::TransformNotificationBus::Handler::BusDisconnect(); + LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect(); + } + + bool TerrainLayerSpawnerComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) + { + if (auto config = azrtti_cast(baseConfig)) + { + m_configuration = *config; + return true; + } + return false; + } + + bool TerrainLayerSpawnerComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + { + if (auto config = azrtti_cast(outBaseConfig)) + { + *config = m_configuration; + return true; + } + return false; + } + + void TerrainLayerSpawnerComponent::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, [[maybe_unused]] const AZ::Transform& world) + { + RefreshArea(); + } + + void TerrainLayerSpawnerComponent::OnShapeChanged([[maybe_unused]] ShapeChangeReasons changeReason) + { + RefreshArea(); + } + + void TerrainLayerSpawnerComponent::RegisterArea() + { + TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RegisterArea, GetEntityId()); + } + + void TerrainLayerSpawnerComponent::RefreshArea() + { + TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId()); + } +} diff --git a/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.h b/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.h new file mode 100644 index 0000000000..1f1ae90227 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainLayerSpawnerComponent.h @@ -0,0 +1,97 @@ +/* + * 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 +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +namespace LmbrCentral +{ + template + class EditorWrappedComponentBase; +} + +namespace Terrain +{ + namespace AreaConstants + { + static const AZ::u32 s_backgroundLayer = 0; + static const AZ::u32 s_foregroundLayer = 1; + static const AZ::u32 s_priorityMin = 0; + static const AZ::u32 s_priorityMax = 10000; //arbitrary number because std::numeric_limits::max() always dislays -1 in RPE + static const AZ::u32 s_prioritySoftMax = 100; //design specified slider range + } + + class TerrainLayerSpawnerConfig + : public AZ::ComponentConfig + { + public: + AZ_CLASS_ALLOCATOR(TerrainLayerSpawnerConfig, AZ::SystemAllocator, 0); + AZ_RTTI(TerrainLayerSpawnerConfig, "{8E0695DE-E843-4858-BAEA-70953E74C810}", AZ::ComponentConfig); + static void Reflect(AZ::ReflectContext* context); + + AZStd::vector> GetSelectableLayers() const; + AZ::u32 m_layer = AreaConstants::s_foregroundLayer; + AZ::u32 m_priority = AreaConstants::s_priorityMin; + bool m_useGroundPlane = true; + }; + + + class TerrainLayerSpawnerComponent + : public AZ::Component + , private AZ::TransformNotificationBus::Handler + , private LmbrCentral::ShapeComponentNotificationsBus::Handler + , private Terrain::TerrainAreaRequestBus::Handler + { + public: + template + friend class LmbrCentral::EditorWrappedComponentBase; + AZ_COMPONENT(TerrainLayerSpawnerComponent, "{3848605F-A4EA-478C-B710-84AB8DCA9EC5}"); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void Reflect(AZ::ReflectContext* context); + + TerrainLayerSpawnerComponent(const TerrainLayerSpawnerConfig& configuration); + TerrainLayerSpawnerComponent() = default; + ~TerrainLayerSpawnerComponent() = default; + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override; + bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; + + + ////////////////////////////////////////////////////////////////////////// + // AZ::TransformNotificationBus::Handler + void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override; + + // ShapeComponentNotificationsBus + void OnShapeChanged(ShapeChangeReasons changeReason) override; + + void RegisterArea() override; + void RefreshArea() override; + + private: + TerrainLayerSpawnerConfig m_configuration; + }; +} diff --git a/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp index dea7babbbc..afaf0470b4 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainSystemComponent.cpp @@ -14,6 +14,8 @@ #include #include +#include +#include namespace Terrain { @@ -32,6 +34,8 @@ namespace Terrain ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ; } + + Terrain::TerrainFeatureProcessor::Reflect(context); } } @@ -60,9 +64,18 @@ namespace Terrain void TerrainSystemComponent::Activate() { + // Currently, the Terrain System Component owns the Terrain System instance because the Terrain World component gets recreated + // every time an entity is added or removed to a level. If this ever changes, the Terrain System ownership could move into + // the level component. + m_terrainSystem = new TerrainSystem(); + AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor(); } void TerrainSystemComponent::Deactivate() { + delete m_terrainSystem; + m_terrainSystem = nullptr; + + AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor(); } } diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp new file mode 100644 index 0000000000..e2af58e3eb --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.cpp @@ -0,0 +1,123 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +namespace Terrain +{ + void TerrainWorldConfig::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("WorldMin", &TerrainWorldConfig::m_worldMin) + ->Field("WorldMax", &TerrainWorldConfig::m_worldMax) + ->Field("HeightQueryResolution", &TerrainWorldConfig::m_heightQueryResolution) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class( + "Terrain World Component", "Data required for the terrain system to run") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC_CE("Level") })) + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + + ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainWorldConfig::m_worldMin, "World Bounds (Min)", "") + ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainWorldConfig::m_worldMax, "World Bounds (Max)", "") + ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainWorldConfig::m_heightQueryResolution, "Height Query Resolution (m)", "") + ; + } + } + } + + void TerrainWorldComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainService")); + } + + void TerrainWorldComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainService")); + } + + void TerrainWorldComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& services) + { + } + + void TerrainWorldComponent::Reflect(AZ::ReflectContext* context) + { + TerrainWorldConfig::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(0) + ->Field("Configuration", &TerrainWorldComponent::m_configuration) + ; + } + } + + TerrainWorldComponent::TerrainWorldComponent(const TerrainWorldConfig& configuration) + : m_configuration(configuration) + { + } + + TerrainWorldComponent::~TerrainWorldComponent() + { + } + + void TerrainWorldComponent::Activate() + { + TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::SetWorldMin, m_configuration.m_worldMin); + TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::SetWorldMax, m_configuration.m_worldMax); + TerrainSystemServiceRequestBus::Broadcast( + &TerrainSystemServiceRequestBus::Events::SetHeightQueryResolution, m_configuration.m_heightQueryResolution); + + // Currently, the Terrain System Component owns the Terrain System instance because the Terrain World component gets recreated + // every time an entity is added or removed to a level. If this ever changes, the Terrain System ownership could move into + // the level component. + TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::Activate); + } + + void TerrainWorldComponent::Deactivate() + { + TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::Deactivate); + } + + bool TerrainWorldComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) + { + if (auto config = azrtti_cast(baseConfig)) + { + m_configuration = *config; + return true; + } + return false; + } + + bool TerrainWorldComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + { + if (auto config = azrtti_cast(outBaseConfig)) + { + *config = m_configuration; + return true; + } + return false; + } +} diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.h b/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.h new file mode 100644 index 0000000000..2dfe1135c8 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldComponent.h @@ -0,0 +1,64 @@ +/* + * 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 +#include +#include +#include + +namespace LmbrCentral +{ + template + class EditorWrappedComponentBase; +} + +namespace Terrain +{ + class TerrainWorldConfig + : public AZ::ComponentConfig + { + public: + AZ_CLASS_ALLOCATOR(TerrainWorldConfig, AZ::SystemAllocator, 0); + AZ_RTTI(TerrainWorldConfig, "{295844DB-20DD-45B2-94DB-4245D5AE9AFF}", AZ::ComponentConfig); + static void Reflect(AZ::ReflectContext* context); + + AZ::Vector3 m_worldMin{ 0.0f, 0.0f, 0.0f }; + AZ::Vector3 m_worldMax{ 1024.0f, 1024.0f, 1024.0f }; + AZ::Vector2 m_heightQueryResolution{ 1.0f, 1.0f }; + }; + + + class TerrainWorldComponent + : public AZ::Component + { + public: + template + friend class LmbrCentral::EditorWrappedComponentBase; + AZ_COMPONENT(TerrainWorldComponent, "{4734EFDC-135D-4BF5-BE57-4F9AD03ADF78}"); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void Reflect(AZ::ReflectContext* context); + + TerrainWorldComponent(const TerrainWorldConfig& configuration); + TerrainWorldComponent() = default; + ~TerrainWorldComponent() override; + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override; + bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; + + private: + TerrainWorldConfig m_configuration; + }; +} diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp new file mode 100644 index 0000000000..1fea51ded4 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp @@ -0,0 +1,322 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace Terrain +{ + void TerrainWorldDebuggerConfig::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("DebugWireframe", &TerrainWorldDebuggerConfig::m_drawWireframe) + ->Field("DebugWorldBounds", &TerrainWorldDebuggerConfig::m_drawWorldBounds) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class( + "Terrain World Debugger Component", "Optional component for enabling terrain debugging features.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC_CE("Level") })) + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + + ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainWorldDebuggerConfig::m_drawWireframe, "Show Wireframe", "") + ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainWorldDebuggerConfig::m_drawWorldBounds, "Show World Bounds", ""); + } + } + } + + void TerrainWorldDebuggerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainDebugService")); + } + + void TerrainWorldDebuggerComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainDebugService")); + } + + void TerrainWorldDebuggerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) + { + services.push_back(AZ_CRC_CE("TerrainService")); + } + + void TerrainWorldDebuggerComponent::Reflect(AZ::ReflectContext* context) + { + TerrainWorldDebuggerConfig::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(0) + ->Field("Configuration", &TerrainWorldDebuggerComponent::m_configuration) + ; + } + } + + TerrainWorldDebuggerComponent::TerrainWorldDebuggerComponent(const TerrainWorldDebuggerConfig& configuration) + : m_configuration(configuration) + { + } + + TerrainWorldDebuggerComponent::~TerrainWorldDebuggerComponent() + { + } + + void TerrainWorldDebuggerComponent::Activate() + { + m_wireframeBounds = AZ::Aabb::CreateNull(); + + TerrainSystemServiceRequestBus::Broadcast( + &TerrainSystemServiceRequestBus::Events::SetDebugWireframe, m_configuration.m_drawWireframe); + + AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); + AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId()); + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); + + } + + void TerrainWorldDebuggerComponent::Deactivate() + { + TerrainSystemServiceRequestBus::Broadcast( + &TerrainSystemServiceRequestBus::Events::SetDebugWireframe, false); + + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); + AzFramework::BoundsRequestBus::Handler::BusDisconnect(); + AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); + + m_wireframeBounds = AZ::Aabb::CreateNull(); + m_wireframeSectors.clear(); + } + + bool TerrainWorldDebuggerComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) + { + if (auto config = azrtti_cast(baseConfig)) + { + m_configuration = *config; + return true; + } + return false; + } + + bool TerrainWorldDebuggerComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const + { + if (auto config = azrtti_cast(outBaseConfig)) + { + *config = m_configuration; + return true; + } + return false; + } + + AZ::Aabb TerrainWorldDebuggerComponent::GetWorldBounds() + { + AZ::Aabb terrainAabb = AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero()); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + terrainAabb, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb); + + return terrainAabb; + } + + AZ::Aabb TerrainWorldDebuggerComponent::GetLocalBounds() + { + // This is a level component, so the local bounds will always be the same as the world bounds. + return GetWorldBounds(); + } + + void TerrainWorldDebuggerComponent::DisplayEntityViewport( + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + { + // Draw a wireframe box around the entire terrain world bounds + if (m_configuration.m_drawWorldBounds) + { + AZ::Color outlineColor(1.0f, 0.0f, 0.0f, 1.0f); + AZ::Aabb aabb = GetWorldBounds(); + + debugDisplay.SetColor(outlineColor); + debugDisplay.DrawWireBox(aabb.GetMin(), aabb.GetMax()); + } + + // Draw a wireframe representation of the terrain surface + if (m_configuration.m_drawWireframe && !m_wireframeSectors.empty()) + { + // Start by assuming we'll draw the entire world. + AZ::Aabb drawingAabb = GetWorldBounds(); + + // Assuming we can get the camera, reduce the drawing bounds to a fixed distance around the camera. + if (auto viewportContextRequests = AZ::RPI::ViewportContextRequests::Get(); viewportContextRequests) + { + // Get the current camera position. + AZ::RPI::ViewportContextPtr viewportContext = viewportContextRequests->GetViewportContextById(viewportInfo.m_viewportId); + AZ::Vector3 cameraPos = viewportContext->GetCameraTransform().GetTranslation(); + + // Determine how far to draw in each direction in world space based on our MaxSectorsToDraw + AZ::Vector2 queryResolution = AZ::Vector2(1.0f); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainGridResolution); + AZ::Vector3 viewDistance( + queryResolution.GetX() * SectorSizeInGridPoints * sqrtf(MaxSectorsToDraw), + queryResolution.GetY() * SectorSizeInGridPoints * sqrtf(MaxSectorsToDraw), + 0.0f); + + // Create an AABB around the camera based on how far we want to be able to draw in each direction and clamp the + // drawing AABB to it. + AZ::Aabb cameraAabb = AZ::Aabb::CreateFromMinMax( + AZ::Vector3( + cameraPos.GetX() - viewDistance.GetX(), cameraPos.GetY() - viewDistance.GetY(), drawingAabb.GetMin().GetZ()), + AZ::Vector3( + cameraPos.GetX() + viewDistance.GetX(), cameraPos.GetY() + viewDistance.GetY(), drawingAabb.GetMin().GetZ())); + drawingAabb.Clamp(cameraAabb); + } + + // For each sector, if it appears within our view distance, draw it. + for (auto& sector : m_wireframeSectors) + { + if (drawingAabb.Overlaps(sector.m_aabb)) + { + if (!sector.m_lineVertices.empty()) + { + const AZ::Color primaryColor = AZ::Color(0.25f, 0.25f, 0.25f, 1.0f); + debugDisplay.DrawLines(sector.m_lineVertices, primaryColor); + } + else + { + AZ_Warning("Debug", false, "empty sector!"); + } + } + } + } + } + + void TerrainWorldDebuggerComponent::RefreshCachedWireframeGrid(const AZ::Aabb& dirtyRegion) + { + // Get the terrain world bounds and grid resolution. + + AZ::Aabb worldBounds = GetWorldBounds(); + + AZ::Vector2 queryResolution = AZ::Vector2(1.0f); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainGridResolution); + + // Calculate the world size of each sector. Note that this size actually ends at the last point, not the last square. + // So for example, the sector size for 3 points will go from (*--*--*) even though it will be used to draw (*--*--*--). + const float xSectorSize = (queryResolution.GetX() * SectorSizeInGridPoints); + const float ySectorSize = (queryResolution.GetY() * SectorSizeInGridPoints); + + // Calculate the total number of sectors to cache. The world bounds might not be evenly divisible by sector bounds, so we add + // an extra sector's worth of size in each direction so that clamping down to an integer still accounts for that fractional sector. + const int32_t numSectorsX = aznumeric_cast((worldBounds.GetXExtent() + xSectorSize) / xSectorSize); + const int32_t numSectorsY = aznumeric_cast((worldBounds.GetYExtent() + ySectorSize) / ySectorSize); + + // If we haven't cached anything before, or if the world bounds has changed, clear our cache structure and repopulate it + // with WireframeSector entries with the proper AABB sizes. + if (!m_wireframeBounds.IsValid() || !dirtyRegion.IsValid() || !m_wireframeBounds.IsClose(worldBounds)) + { + m_wireframeBounds = worldBounds; + + m_wireframeSectors.clear(); + m_wireframeSectors.reserve(numSectorsX * numSectorsY); + + for (int32_t ySector = 0; ySector < numSectorsY; ySector++) + { + for (int32_t xSector = 0; xSector < numSectorsX; xSector++) + { + // For each sector, set up the AABB for the sector and reserve memory for the line vertices. + WireframeSector sector; + sector.m_lineVertices.reserve(VerticesPerSector); + sector.m_aabb = AZ::Aabb::CreateFromMinMax( + AZ::Vector3( + worldBounds.GetMin().GetX() + (xSector * xSectorSize), worldBounds.GetMin().GetY() + (ySector * ySectorSize), + worldBounds.GetMin().GetZ()), + AZ::Vector3( + worldBounds.GetMin().GetX() + ((xSector + 1) * xSectorSize), + worldBounds.GetMin().GetY() + ((ySector + 1) * ySectorSize), worldBounds.GetMax().GetZ())); + + sector.m_aabb.Clamp(worldBounds); + + m_wireframeSectors.push_back(AZStd::move(sector)); + } + } + + // Notify the visibility system that our bounds have changed. + AzFramework::IEntityBoundsUnionRequestBus::Broadcast( + &AzFramework::IEntityBoundsUnionRequestBus::Events::RefreshEntityLocalBoundsUnion, GetEntityId()); + } + + // For each sector, if it overlaps with the dirty region, clear it out and recache the wireframe line data. + for (auto& sector : m_wireframeSectors) + { + if (dirtyRegion.IsValid() && !dirtyRegion.Overlaps(sector.m_aabb)) + { + continue; + } + + sector.m_lineVertices.clear(); + + for (float y = sector.m_aabb.GetMin().GetY(); y < sector.m_aabb.GetMax().GetY(); y += queryResolution.GetY()) + { + for (float x = sector.m_aabb.GetMin().GetX(); x < sector.m_aabb.GetMax().GetX(); x += queryResolution.GetX()) + { + float x1 = x + queryResolution.GetX(); + float y1 = y + queryResolution.GetY(); + + float z00 = 0.0f; + float z01 = 0.0f; + float z10 = 0.0f; + bool terrainExists; + + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + z00, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x, y, + AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT, &terrainExists); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + z01, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x, y1, + AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT, &terrainExists); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + z10, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x1, y, + AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT, &terrainExists); + + sector.m_lineVertices.push_back(AZ::Vector3(x, y, z00)); + sector.m_lineVertices.push_back(AZ::Vector3(x1, y, z10)); + + sector.m_lineVertices.push_back(AZ::Vector3(x, y, z00)); + sector.m_lineVertices.push_back(AZ::Vector3(x, y1, z01)); + } + } + } + } + + void TerrainWorldDebuggerComponent::OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) + { + if (dataChangedMask & (TerrainDataChangedMask::Settings | TerrainDataChangedMask::HeightData)) + { + RefreshCachedWireframeGrid(dirtyRegion); + } + } + + +} // namespace Terrain diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.h b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.h new file mode 100644 index 0000000000..5ec831c038 --- /dev/null +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.h @@ -0,0 +1,114 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +namespace LmbrCentral +{ + template + class EditorWrappedComponentBase; +} + +namespace Terrain +{ + class TerrainWorldDebuggerConfig + : public AZ::ComponentConfig + { + public: + AZ_CLASS_ALLOCATOR(TerrainWorldDebuggerConfig, AZ::SystemAllocator, 0); + AZ_RTTI(TerrainWorldDebuggerConfig, "{92686FA9-2C0B-47F1-8E2D-F2F302CDE5AA}", AZ::ComponentConfig); + static void Reflect(AZ::ReflectContext* context); + + bool m_drawWireframe{ true }; + bool m_drawWorldBounds{ true }; + }; + + + class TerrainWorldDebuggerComponent + : public AZ::Component + , private AzFramework::EntityDebugDisplayEventBus::Handler + , private AzFramework::BoundsRequestBus::Handler + , private AzFramework::Terrain::TerrainDataNotificationBus::Handler + { + public: + template + friend class LmbrCentral::EditorWrappedComponentBase; + AZ_COMPONENT(TerrainWorldDebuggerComponent, "{ECA1F4CB-5395-41FD-B6ED-FFD2C80096E2}"); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services); + static void Reflect(AZ::ReflectContext* context); + + TerrainWorldDebuggerComponent(const TerrainWorldDebuggerConfig& configuration); + TerrainWorldDebuggerComponent() = default; + ~TerrainWorldDebuggerComponent() override; + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override; + bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override; + + ////////////////////////////////////////////////////////////////////////// + // EntityDebugDisplayEventBus + + // Ideally this would use ViewportDebugDisplayEventBus::DisplayViewport, but that doesn't currently work in game mode, + // so instead we use this plus the BoundsRequestBus with a large AABB to get ourselves rendered. + void DisplayEntityViewport( + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; + + ////////////////////////////////////////////////////////////////////////// + // BoundsRequestBus + AZ::Aabb GetWorldBounds() override; + AZ::Aabb GetLocalBounds() override; + + ////////////////////////////////////////////////////////////////////////// + // AzFramework::Terrain::TerrainDataNotificationBus + void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override; + + private: + + // Cache our debug wireframe representation in "sectors" of data so that we can easily control how far out we draw + // the wireframe representation in each direction. + struct WireframeSector + { + AZ::Aabb m_aabb; + AZStd::vector m_lineVertices; + }; + + // Each sector contains an N x N grid of squares that it will draw. Since this is a count of the number of terrain grid points + // in each direction, the actual world size will depend on the terrain grid resolution in each direction. + static constexpr int32_t SectorSizeInGridPoints = 10; + + // For each grid point we will draw half a square (left-right, top-down), so we need 4 vertices for the two lines. + static constexpr int32_t VerticesPerGridPoint = 4; + + // Pre-calculate the total number of vertices per sector. + static constexpr int32_t VerticesPerSector = + (SectorSizeInGridPoints * VerticesPerGridPoint) * (SectorSizeInGridPoints * VerticesPerGridPoint); + + // AuxGeom has limits to the number of lines it can draw in a frame, so we'll cap how many total sectors to draw. + static constexpr int32_t MaxVerticesToDraw = 500000; + static constexpr int32_t MaxSectorsToDraw = MaxVerticesToDraw / VerticesPerSector; + + void RefreshCachedWireframeGrid(const AZ::Aabb& dirtyRegion); + + TerrainWorldDebuggerConfig m_configuration; + AZStd::vector m_wireframeSectors; + AZ::Aabb m_wireframeBounds; + }; +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainHeightGradientListComponent.cpp b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainHeightGradientListComponent.cpp new file mode 100644 index 0000000000..8a36445446 --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainHeightGradientListComponent.cpp @@ -0,0 +1,22 @@ +/* + * 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 +#include +#include + +namespace Terrain +{ + void EditorTerrainHeightGradientListComponent::Reflect(AZ::ReflectContext* context) + { + BaseClassType::ReflectSubClass(context, 1, + &LmbrCentral::EditorWrappedComponentBaseVersionConverter + ); + } +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainHeightGradientListComponent.h b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainHeightGradientListComponent.h new file mode 100644 index 0000000000..2433eda009 --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainHeightGradientListComponent.h @@ -0,0 +1,32 @@ +/* + * 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 +#include +#include + +namespace Terrain +{ + class EditorTerrainHeightGradientListComponent + : public LmbrCentral::EditorWrappedComponentBase + { + public: + using BaseClassType = LmbrCentral::EditorWrappedComponentBase; + AZ_EDITOR_COMPONENT(EditorTerrainHeightGradientListComponent, "{2D945B90-ADAB-4F9A-A113-39E714708068}", BaseClassType); + static void Reflect(AZ::ReflectContext* context); + + static constexpr const char* const s_categoryName = "Terrain"; + static constexpr const char* const s_componentName = "Terrain Height Gradient List"; + static constexpr const char* const s_componentDescription = "Provides height data for a region to the terrain system"; + static constexpr const char* const s_icon = "Editor/Icons/Components/TerrainHeight.svg"; + static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/TerrainHeight.svg"; + static constexpr const char* const s_helpUrl = ""; + }; +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainLayerSpawnerComponent.cpp b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainLayerSpawnerComponent.cpp new file mode 100644 index 0000000000..3a0208aece --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainLayerSpawnerComponent.cpp @@ -0,0 +1,22 @@ +/* + * 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 +#include +#include + +namespace Terrain +{ + void EditorTerrainLayerSpawnerComponent::Reflect(AZ::ReflectContext* context) + { + BaseClassType::ReflectSubClass(context, 1, + &LmbrCentral::EditorWrappedComponentBaseVersionConverter + ); + } +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainLayerSpawnerComponent.h b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainLayerSpawnerComponent.h new file mode 100644 index 0000000000..1eb3413d52 --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainLayerSpawnerComponent.h @@ -0,0 +1,32 @@ +/* + * 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 +#include +#include + +namespace Terrain +{ + class EditorTerrainLayerSpawnerComponent + : public LmbrCentral::EditorWrappedComponentBase + { + public: + using BaseClassType = LmbrCentral::EditorWrappedComponentBase; + AZ_EDITOR_COMPONENT(EditorTerrainLayerSpawnerComponent, "{9403FC94-FA38-4387-BEFD-A728C7D850C1}", BaseClassType); + static void Reflect(AZ::ReflectContext* context); + + static constexpr const char* const s_categoryName = "Terrain"; + static constexpr const char* const s_componentName = "Terrain Layer Spawner"; + static constexpr const char* const s_componentDescription = "Defines a terrain region for use by the terrain system"; + static constexpr const char* const s_icon = "Editor/Icons/Components/TerrainLayerSpawner.svg"; + static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/TerrainLayerSpawner.svg"; + static constexpr const char* const s_helpUrl = ""; + }; +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldComponent.cpp b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldComponent.cpp new file mode 100644 index 0000000000..a50a44745f --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldComponent.cpp @@ -0,0 +1,56 @@ +/* + * 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 +#include +#include + +namespace Terrain +{ + void EditorTerrainWorldComponent::Reflect(AZ::ReflectContext* context) + { + BaseClassType::Reflect(context); + + AZ::SerializeContext* serializeContext = azrtti_cast(context); + + if (serializeContext) + { + serializeContext->Class() + ->Version(0) + ; + + if (auto editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Terrain World", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Terrain") + ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/TerrainWorld.svg") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/TerrainWorld.svg") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC_CE("Level") })) + ; + } + } + } + + + void EditorTerrainWorldComponent::Init() + { + BaseClassType::Init(); + } + + void EditorTerrainWorldComponent::Activate() + { + BaseClassType::Activate(); + } + + AZ::u32 EditorTerrainWorldComponent::ConfigurationChanged() + { + return BaseClassType::ConfigurationChanged(); + } +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldComponent.h b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldComponent.h new file mode 100644 index 0000000000..6f3ab73361 --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldComponent.h @@ -0,0 +1,38 @@ +/* + * 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 +#include +#include + +namespace Terrain +{ + class EditorTerrainWorldComponent + : public LmbrCentral::EditorWrappedComponentBase + { + public: + using BaseClassType = LmbrCentral::EditorWrappedComponentBase; + AZ_EDITOR_COMPONENT(EditorTerrainWorldComponent, "{43D02ADC-111F-4584-B590-FF6DC9FC912C}", BaseClassType); + static void Reflect(AZ::ReflectContext* context); + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Init() override; + void Activate() override; + AZ::u32 ConfigurationChanged() override; + + protected: + using BaseClassType::m_configuration; + using BaseClassType::m_component; + using BaseClassType::m_visible; + + private: + }; +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldDebuggerComponent.cpp b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldDebuggerComponent.cpp new file mode 100644 index 0000000000..7e8fbf58e4 --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldDebuggerComponent.cpp @@ -0,0 +1,56 @@ +/* + * 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 +#include +#include + +namespace Terrain +{ + void EditorTerrainWorldDebuggerComponent::Reflect(AZ::ReflectContext* context) + { + BaseClassType::Reflect(context); + + AZ::SerializeContext* serializeContext = azrtti_cast(context); + + if (serializeContext) + { + serializeContext->Class() + ->Version(0) + ; + + if (auto editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Terrain World Debugger", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Terrain") + ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/TerrainWorldDebugger.svg") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/TerrainWorldDebugger.svg") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector({ AZ_CRC_CE("Level") })) + ; + } + } + } + + + void EditorTerrainWorldDebuggerComponent::Init() + { + BaseClassType::Init(); + } + + void EditorTerrainWorldDebuggerComponent::Activate() + { + BaseClassType::Activate(); + } + + AZ::u32 EditorTerrainWorldDebuggerComponent::ConfigurationChanged() + { + return BaseClassType::ConfigurationChanged(); + } +} diff --git a/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldDebuggerComponent.h b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldDebuggerComponent.h new file mode 100644 index 0000000000..ddcfe3c76a --- /dev/null +++ b/Gems/Terrain/Code/Source/EditorComponents/EditorTerrainWorldDebuggerComponent.h @@ -0,0 +1,38 @@ +/* + * 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 +#include +#include + +namespace Terrain +{ + class EditorTerrainWorldDebuggerComponent + : public LmbrCentral::EditorWrappedComponentBase + { + public: + using BaseClassType = LmbrCentral::EditorWrappedComponentBase; + AZ_EDITOR_COMPONENT(EditorTerrainWorldDebuggerComponent, "{D09BA0B9-FB51-446B-BD7B-3C40743D2E39}", BaseClassType); + static void Reflect(AZ::ReflectContext* context); + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Init() override; + void Activate() override; + AZ::u32 ConfigurationChanged() override; + + protected: + using BaseClassType::m_configuration; + using BaseClassType::m_component; + using BaseClassType::m_visible; + + private: + }; +} diff --git a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp index bd4c94f4bf..0ba416cbb7 100644 --- a/Gems/Terrain/Code/Source/EditorTerrainModule.cpp +++ b/Gems/Terrain/Code/Source/EditorTerrainModule.cpp @@ -7,7 +7,11 @@ */ #include +#include +#include #include +#include +#include namespace Terrain { @@ -16,7 +20,12 @@ namespace Terrain m_descriptors.insert( m_descriptors.end(), { + Terrain::EditorTerrainHeightGradientListComponent::CreateDescriptor(), + Terrain::EditorTerrainLayerSpawnerComponent::CreateDescriptor(), Terrain::EditorTerrainSystemComponent::CreateDescriptor(), + Terrain::EditorTerrainWorldComponent::CreateDescriptor(), + Terrain::EditorTerrainWorldDebuggerComponent::CreateDescriptor(), + }); } diff --git a/Gems/Terrain/Code/Source/TerrainModule.cpp b/Gems/Terrain/Code/Source/TerrainModule.cpp index ec9325eb5c..29b2542472 100644 --- a/Gems/Terrain/Code/Source/TerrainModule.cpp +++ b/Gems/Terrain/Code/Source/TerrainModule.cpp @@ -11,6 +11,10 @@ #include #include +#include +#include +#include +#include #include namespace Terrain @@ -20,6 +24,10 @@ namespace Terrain { m_descriptors.insert(m_descriptors.end(), { TerrainSystemComponent::CreateDescriptor(), + TerrainWorldComponent::CreateDescriptor(), + TerrainWorldDebuggerComponent::CreateDescriptor(), + TerrainHeightGradientListComponent::CreateDescriptor(), + TerrainLayerSpawnerComponent::CreateDescriptor(), TerrainSurfaceDataSystemComponent::CreateDescriptor(), }); } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp new file mode 100644 index 0000000000..5af3fd58b9 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -0,0 +1,490 @@ +/* + * 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 + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace Terrain +{ + namespace + { + const uint32_t DEFAULT_UploadBufferSize = 512 * 1024; // 512k + } + + namespace ShaderInputs + { + static const char* const HeightmapImage("HeightmapImage"); + static const char* const ModelToWorld("m_modelToWorld"); + static const char* const HeightScale("m_heightScale"); + static const char* const UvMin("m_uvMin"); + static const char* const UvMax("m_uvMax"); + static const char* const UvStep("m_uvStep"); + } + + + void TerrainFeatureProcessor::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(0) + ; + } + } + + void TerrainFeatureProcessor::Activate() + { + m_areaData.clear(); + + InitializeAtomStuff(); + EnableSceneNotification(); + } + + void TerrainFeatureProcessor::InitializeAtomStuff() + { + m_rhiSystem = AZ::RHI::RHISystemInterface::Get(); + + m_rhiSystem->GetDrawListTagRegistry()->AcquireTag(AZ::Name("Terrain")); + + { + // Load the shader + + const char* terrainShaderFilePath = "Shaders/Terrain/Terrain.azshader"; + + AZ::Data::AssetId shaderAssetId; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + shaderAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, + terrainShaderFilePath, azrtti_typeid(), false); + if (!shaderAssetId.IsValid()) + { + AZ_Error("Terrain", false, "Failed to get shader asset id with path %s", terrainShaderFilePath); + return; + } + + auto shaderAsset = AZ::Data::AssetManager::Instance().GetAsset(shaderAssetId, AZ::Data::AssetLoadBehavior::PreLoad); + shaderAsset.BlockUntilLoadComplete(); + + if (!shaderAsset.IsReady()) + { + AZ_Error("Terrain", false, "Failed to get shader asset with path %s", terrainShaderFilePath); + return; + } + + m_shader = AZ::RPI::Shader::FindOrCreate(shaderAsset); + if (!m_shader) + { + AZ_Error("Terrain", false, "Failed to find or create a shader instance from shader asset '%s'", terrainShaderFilePath); + return; + } + + // Create the data layout + + m_pipelineStateDescriptor = AZ::RHI::PipelineStateDescriptorForDraw{}; + + { + AZ::RHI::InputStreamLayoutBuilder layoutBuilder; + + layoutBuilder.AddBuffer() + ->Channel("POSITION", AZ::RHI::Format::R32G32_FLOAT) + ->Channel("UV", AZ::RHI::Format::R32G32_FLOAT) + ; + m_pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End(); + } + + auto shaderVariant = m_shader->GetVariant(AZ::RPI::ShaderAsset::RootShaderVariantStableId); + shaderVariant.ConfigurePipelineState(m_pipelineStateDescriptor); + + m_drawListTag = m_shader->GetDrawListTag(); + + m_perObjectSrgAsset = m_shader->FindShaderResourceGroupLayout(AZ::Name{"ObjectSrg"}); + if (!m_perObjectSrgAsset) + { + AZ_Error("Terrain", false, "Failed to get shader resource group asset"); + return; + } + else if (!m_perObjectSrgAsset->IsFinalized()) + { + AZ_Error("Terrain", false, "Shader resource group asset is not loaded"); + return; + } + + const AZ::RHI::ShaderResourceGroupLayout* shaderResourceGroupLayout = &(*m_perObjectSrgAsset); + + m_heightmapImageIndex = shaderResourceGroupLayout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::HeightmapImage)); + AZ_Error("Terrain", m_heightmapImageIndex.IsValid(), "Failed to find shader input image %s.", ShaderInputs::HeightmapImage); + + m_modelToWorldIndex = shaderResourceGroupLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::ModelToWorld)); + AZ_Error("Terrain", m_modelToWorldIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::ModelToWorld); + + m_heightScaleIndex = shaderResourceGroupLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::HeightScale)); + AZ_Error("Terrain", m_heightScaleIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::HeightScale); + + m_uvMinIndex = shaderResourceGroupLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::UvMin)); + AZ_Error("Terrain", m_uvMinIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::UvMin); + + m_uvMaxIndex = shaderResourceGroupLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::UvMax)); + AZ_Error("Terrain", m_uvMaxIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::UvMax); + + m_uvStepIndex = shaderResourceGroupLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::UvStep)); + AZ_Error("Terrain", m_uvStepIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::UvStep); + + // If this fails to run now, it's ok, we'll initialize it in OnRenderPipelineAdded later. + bool success = GetParentScene()->ConfigurePipelineState(m_shader->GetDrawListTag(), m_pipelineStateDescriptor); + if (success) + { + m_pipelineState = m_shader->AcquirePipelineState(m_pipelineStateDescriptor); + AZ_Assert(m_pipelineState, "Failed to acquire default pipeline state for shader '%s'", terrainShaderFilePath); + } + } + + AZ::RHI::BufferPoolDescriptor dmaPoolDescriptor; + dmaPoolDescriptor.m_heapMemoryLevel = AZ::RHI::HeapMemoryLevel::Host; + dmaPoolDescriptor.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly; + + m_hostPool = AZ::RHI::Factory::Get().CreateBufferPool(); + m_hostPool->SetName(AZ::Name("TerrainVertexPool")); + AZ::RHI::ResultCode resultCode = m_hostPool->Init(*m_rhiSystem->GetDevice(), dmaPoolDescriptor); + + if (resultCode != AZ::RHI::ResultCode::Success) + { + AZ_Error("Terrain", false, "Failed to create host buffer pool from RPI"); + return; + } + + InitializeTerrainPatch(); + + if (!InitializeRenderBuffers()) + { + AZ_Error("Terrain", false, "Failed to create Terrain render buffers!"); + return; + } + } + + void TerrainFeatureProcessor::OnRenderPipelineAdded([[maybe_unused]] AZ::RPI::RenderPipelinePtr pipeline) + { + bool success = GetParentScene()->ConfigurePipelineState(m_drawListTag, m_pipelineStateDescriptor); + AZ_Assert(success, "Couldn't configure the pipeline state."); + if (success) + { + m_pipelineState = m_shader->AcquirePipelineState(m_pipelineStateDescriptor); + AZ_Assert(m_pipelineState, "Failed to acquire default pipeline state."); + } + } + + void TerrainFeatureProcessor::OnRenderPipelineRemoved([[maybe_unused]] AZ::RPI::RenderPipeline* pipeline) + { + } + + void TerrainFeatureProcessor::OnRenderPipelinePassesChanged([[maybe_unused]] AZ::RPI::RenderPipeline* renderPipeline) + { + } + + + void TerrainFeatureProcessor::Deactivate() + { + DisableSceneNotification(); + + DestroyRenderBuffers(); + m_areaData.clear(); + + if (m_hostPool) + { + m_hostPool.reset(); + } + + m_rhiSystem = nullptr; + } + + void TerrainFeatureProcessor::Render(const AZ::RPI::FeatureProcessor::RenderPacket& packet) + { + ProcessSurfaces(packet); + } + + void TerrainFeatureProcessor::UpdateTerrainData( + AZ::EntityId areaId, + const AZ::Transform& transform, + const AZ::Aabb& worldBounds, + [[maybe_unused]] float sampleSpacing, + uint32_t width, uint32_t height, const AZStd::vector& heightData) + { + if (!worldBounds.IsValid()) + { + m_areaData.erase(areaId); + return; + } + + TerrainAreaData areaData; + + areaData.m_transform = transform; + areaData.m_heightScale = worldBounds.GetZExtent(); + areaData.m_terrainBounds = worldBounds; + areaData.m_heightmapImageHeight = height; + areaData.m_heightmapImageWidth = width; + + // Create heightmap image data + { + areaData.m_propertiesDirty = true; + + AZ::RHI::Size imageSize; + imageSize.m_width = width; + imageSize.m_height = height; + + AZ::Data::Instance streamingImagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemStreamingPool(); + areaData.m_heightmapImage = AZ::RPI::StreamingImage::CreateFromCpuData(*streamingImagePool, + AZ::RHI::ImageDimension::Image2D, + imageSize, + AZ::RHI::Format::R32_FLOAT, + (uint8_t*)heightData.data(), + heightData.size() * sizeof(float)); + AZ_Error("Terrain", areaData.m_heightmapImage, "Failed to initialize the heightmap image!"); + } + + m_areaData.insert_or_assign(areaId, areaData); + } + + void TerrainFeatureProcessor::ProcessSurfaces(const FeatureProcessor::RenderPacket& process) + { + AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzRender); + + if (m_drawListTag.IsNull()) + { + return; + } + + if (m_areaData.empty()) + { + return; + } + + m_drawPackets.clear(); + m_processSrgs.clear(); + + AZ::RHI::DrawPacketBuilder drawPacketBuilder; + + uint32_t numIndices = static_cast(m_gridIndices.size()); + + AZ::RHI::DrawIndexed drawIndexed; + drawIndexed.m_indexCount = numIndices; + drawIndexed.m_indexOffset = 0; + drawIndexed.m_vertexOffset = 0; + + for (auto& [areaId, areaData] : m_areaData) + { + float xFirstPatchStart = + areaData.m_terrainBounds.GetMin().GetX() - fmod(areaData.m_terrainBounds.GetMin().GetX(), m_gridMeters); + float xLastPatchStart = areaData.m_terrainBounds.GetMax().GetX() - fmod(areaData.m_terrainBounds.GetMax().GetX(), m_gridMeters); + float yFirstPatchStart = + areaData.m_terrainBounds.GetMin().GetY() - fmod(areaData.m_terrainBounds.GetMin().GetY(), m_gridMeters); + float yLastPatchStart = areaData.m_terrainBounds.GetMax().GetY() - fmod(areaData.m_terrainBounds.GetMax().GetY(), m_gridMeters); + + for (float yPatch = yFirstPatchStart; yPatch <= yLastPatchStart; yPatch += m_gridMeters) + { + for (float xPatch = xFirstPatchStart; xPatch <= xLastPatchStart; xPatch += m_gridMeters) + { + drawPacketBuilder.Begin(nullptr); + drawPacketBuilder.SetDrawArguments(drawIndexed); + drawPacketBuilder.SetIndexBufferView(m_indexBufferView); + + auto m_resourceGroup = AZ::RPI::ShaderResourceGroup::Create(m_shader->GetAsset(), m_shader->GetSupervariantIndex(), AZ::Name("ObjectSrg")); + //auto m_resourceGroup = AZ::RPI::ShaderResourceGroup::Create(m_shader->GetAsset(), AZ::Name("ObjectSrg")); + if (!m_resourceGroup) + { + AZ_Error("Terrain", false, "Failed to create shader resource group"); + return; + } + + float uvMin[2] = { 0.0f, 0.0f }; + float uvMax[2] = { 1.0f, 1.0f }; + + uvMin[0] = (float)((xPatch - areaData.m_terrainBounds.GetMin().GetX()) / areaData.m_terrainBounds.GetXExtent()); + uvMin[1] = (float)((yPatch - areaData.m_terrainBounds.GetMin().GetY()) / areaData.m_terrainBounds.GetYExtent()); + + uvMax[0] = + (float)(((xPatch + m_gridMeters) - areaData.m_terrainBounds.GetMin().GetX()) / areaData.m_terrainBounds.GetXExtent()); + uvMax[1] = + (float)(((yPatch + m_gridMeters) - areaData.m_terrainBounds.GetMin().GetY()) / areaData.m_terrainBounds.GetYExtent()); + + float uvStep[2] = + { + 1.0f / areaData.m_heightmapImageWidth, 1.0f / areaData.m_heightmapImageHeight, + }; + + AZ::Transform transform = areaData.m_transform; + transform.SetTranslation(xPatch, yPatch, areaData.m_transform.GetTranslation().GetZ()); + + AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(transform); + + m_resourceGroup->SetImage(m_heightmapImageIndex, areaData.m_heightmapImage); + m_resourceGroup->SetConstant(m_modelToWorldIndex, matrix3x4); + m_resourceGroup->SetConstant(m_heightScaleIndex, areaData.m_heightScale); + m_resourceGroup->SetConstant(m_uvMinIndex, uvMin); + m_resourceGroup->SetConstant(m_uvMaxIndex, uvMax); + m_resourceGroup->SetConstant(m_uvStepIndex, uvStep); + m_resourceGroup->Compile(); + m_processSrgs.push_back(m_resourceGroup); + + if (m_resourceGroup != nullptr) + { + drawPacketBuilder.AddShaderResourceGroup(m_resourceGroup->GetRHIShaderResourceGroup()); + } + + AZ::RHI::DrawPacketBuilder::DrawRequest drawRequest; + drawRequest.m_listTag = m_drawListTag; + drawRequest.m_pipelineState = m_pipelineState.get(); + drawRequest.m_streamBufferViews = m_vertexBufferViews; + drawPacketBuilder.AddDrawItem(drawRequest); + + const AZ::RHI::DrawPacket* drawPacket = drawPacketBuilder.End(); + m_drawPackets.emplace_back(drawPacket); + + for (auto& view : process.m_views) + { + view->AddDrawPacket(drawPacket); + } + } + } + } + } + + void TerrainFeatureProcessor::InitializeTerrainPatch() + { + m_gridVertices.clear(); + m_gridIndices.clear(); + + for (float y = 0.0f; y < m_gridMeters; y += m_gridSpacing) + { + for (float x = 0.0f; x < m_gridMeters; x += m_gridSpacing) + { + float x0 = x; + float x1 = x + m_gridSpacing; + float y0 = y; + float y1 = y + m_gridSpacing; + + uint16_t startIndex = (uint16_t)(m_gridVertices.size()); + + m_gridVertices.emplace_back(x0, y0, x0 / m_gridMeters, y0 / m_gridMeters); + m_gridVertices.emplace_back(x0, y1, x0 / m_gridMeters, y1 / m_gridMeters); + m_gridVertices.emplace_back(x1, y0, x1 / m_gridMeters, y0 / m_gridMeters); + m_gridVertices.emplace_back(x1, y1, x1 / m_gridMeters, y1 / m_gridMeters); + + m_gridIndices.emplace_back(startIndex); + m_gridIndices.emplace_back(aznumeric_cast(startIndex + 1)); + m_gridIndices.emplace_back(aznumeric_cast(startIndex + 2)); + m_gridIndices.emplace_back(aznumeric_cast(startIndex + 1)); + m_gridIndices.emplace_back(aznumeric_cast(startIndex + 2)); + m_gridIndices.emplace_back(aznumeric_cast(startIndex + 3)); + } + } + } + + bool TerrainFeatureProcessor::InitializeRenderBuffers() + { + AZ::RHI::ResultCode result = AZ::RHI::ResultCode::Fail; + + // Create geometry buffers + m_indexBuffer = AZ::RHI::Factory::Get().CreateBuffer(); + m_vertexBuffer = AZ::RHI::Factory::Get().CreateBuffer(); + + m_indexBuffer->SetName(AZ::Name("TerrainIndexBuffer")); + m_vertexBuffer->SetName(AZ::Name("TerrainVertexBuffer")); + + // We only need one vertex buffer view. + m_vertexBufferViews.resize(1); + + AZStd::vector> buffers = { m_indexBuffer , m_vertexBuffer }; + + // Fill our buffers with the vertex/index data + for (size_t bufferIndex = 0; bufferIndex < buffers.size(); ++bufferIndex) + { + AZ::RHI::Ptr buffer = buffers[bufferIndex]; + + // Initialize the buffer + + AZ::RHI::BufferInitRequest bufferRequest; + bufferRequest.m_descriptor = AZ::RHI::BufferDescriptor{ AZ::RHI::BufferBindFlags::InputAssembly, DEFAULT_UploadBufferSize }; + bufferRequest.m_buffer = buffer.get(); + + result = m_hostPool->InitBuffer(bufferRequest); + + if (result != AZ::RHI::ResultCode::Success) + { + AZ_Error("Terrain", false, "Failed to create GPU buffers for Terrain"); + return false; + } + + // Grab a pointer to the buffer's data + + m_hostPool->OrphanBuffer(*buffer); + + AZ::RHI::BufferMapResponse mapResponse; + m_hostPool->MapBuffer(AZ::RHI::BufferMapRequest(*buffer, 0, DEFAULT_UploadBufferSize), mapResponse); + + auto* mappedData = reinterpret_cast(mapResponse.m_data); + + //0th index should always be the index buffer + if (bufferIndex == 0) + { + // Fill the index buffer with our terrain patch indices + const uint64_t idxSize = m_gridIndices.size() * sizeof(uint16_t); + memcpy(mappedData, m_gridIndices.data(), idxSize); + + m_indexBufferView = AZ::RHI::IndexBufferView( + *buffer, 0, static_cast(idxSize), AZ::RHI::IndexFormat::Uint16); + } + else + { + // Fill the vertex buffer with our terrain patch vertices + const uint64_t elementSize = m_gridVertices.size() * sizeof(Vertex); + memcpy(mappedData, m_gridVertices.data(), elementSize); + + m_vertexBufferViews[bufferIndex - 1] = AZ::RHI::StreamBufferView( + *buffer, 0, static_cast(elementSize), static_cast(sizeof(Vertex))); + + AZ::RHI::ValidateStreamBufferViews(m_pipelineStateDescriptor.m_inputStreamLayout, m_vertexBufferViews); + } + + m_hostPool->UnmapBuffer(*buffer); + } + + return true; + } + + void TerrainFeatureProcessor::DestroyRenderBuffers() + { + m_indexBuffer.reset(); + m_vertexBuffer.reset(); + + m_vertexBufferViews.clear(); + + m_processSrgs.clear(); + + m_pipelineStateDescriptor = AZ::RHI::PipelineStateDescriptorForDraw{}; + m_pipelineState = nullptr; + } + +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h new file mode 100644 index 0000000000..2a1d5b7514 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h @@ -0,0 +1,145 @@ +/* + * 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 +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Terrain +{ + class TerrainFeatureProcessor final + : public AZ::RPI::FeatureProcessor + { + public: + AZ_RTTI(TerrainFeatureProcessor, "{D7DAC1F9-4A9F-4D3C-80AE-99579BF8AB1C}", AZ::RPI::FeatureProcessor); + AZ_DISABLE_COPY_MOVE(TerrainFeatureProcessor); + AZ_FEATURE_PROCESSOR(TerrainFeatureProcessor); + + static void Reflect(AZ::ReflectContext* context); + + TerrainFeatureProcessor() = default; + ~TerrainFeatureProcessor() = default; + + ////////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + void Render(const AZ::RPI::FeatureProcessor::RenderPacket& packet) override; + + void UpdateTerrainData(AZ::EntityId areaId, const AZ::Transform& transform, const AZ::Aabb& worldBounds, float sampleSpacing, + uint32_t width, uint32_t height, const AZStd::vector& heightData); + + void RemoveTerrainData(AZ::EntityId areaId) + { + m_areaData.erase(areaId); + } + void RemoveTerrainData() + { + m_areaData.clear(); + } + + private: + // RPI::SceneNotificationBus overrides ... + void OnRenderPipelineAdded(AZ::RPI::RenderPipelinePtr pipeline) override; + void OnRenderPipelineRemoved(AZ::RPI::RenderPipeline* pipeline) override; + void OnRenderPipelinePassesChanged(AZ::RPI::RenderPipeline* renderPipeline) override; + + void InitializeAtomStuff(); + + void InitializeTerrainPatch(); + + bool InitializeRenderBuffers(); + void DestroyRenderBuffers(); + + void ProcessSurfaces(const FeatureProcessor::RenderPacket& process); + + // System-level parameters + const float m_gridSpacing{ 1.0f }; + const float m_gridMeters{ 32.0f }; + + // System-level cached reference to the Atom RHI + AZ::RHI::RHISystemInterface* m_rhiSystem = nullptr; + + // System-level references to the shader, pipeline, and shader-related information + AZ::Data::Instance m_shader{}; + AZ::RHI::PipelineStateDescriptorForDraw m_pipelineStateDescriptor; + AZ::RHI::ConstPtr m_pipelineState = nullptr; + AZ::RHI::DrawListTag m_drawListTag; + AZ::RHI::Ptr m_perObjectSrgAsset; + + AZ::RHI::ShaderInputImageIndex m_heightmapImageIndex; + AZ::RHI::ShaderInputConstantIndex m_modelToWorldIndex; + AZ::RHI::ShaderInputConstantIndex m_heightScaleIndex; + AZ::RHI::ShaderInputConstantIndex m_uvMinIndex; + AZ::RHI::ShaderInputConstantIndex m_uvMaxIndex; + AZ::RHI::ShaderInputConstantIndex m_uvStepIndex; + + + // Pos_float_2 + UV_float_2 + struct Vertex + { + float m_posx; + float m_posy; + float m_u; + float m_v; + + Vertex(float posx, float posy, float u, float v) + : m_posx(posx) + , m_posy(posy) + , m_u(u) + , m_v(v) + { + } + }; + + // System-level definition of a grid patch. (ex: 32m x 32m) + AZStd::vector m_gridVertices; + AZStd::vector m_gridIndices; + + // System-level data related to the grid patch + AZ::RHI::Ptr m_hostPool = nullptr; + AZ::RHI::Ptr m_indexBuffer; + AZ::RHI::Ptr m_vertexBuffer; + AZ::RHI::IndexBufferView m_indexBufferView; + AZStd::fixed_vector m_vertexBufferViews; + + // Per-area data + struct TerrainAreaData + { + AZ::Transform m_transform; + AZ::Aabb m_terrainBounds; + float m_heightScale; + AZ::Data::Instance m_heightmapImage; + uint32_t m_heightmapImageWidth; + uint32_t m_heightmapImageHeight; + bool m_propertiesDirty{ true }; + }; + + AZStd::unordered_map m_areaData; + + // These could either be per-area or system-level + AZStd::vector> m_drawPackets; + AZStd::vector> m_processSrgs; + }; +} diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp new file mode 100644 index 0000000000..872b37a02e --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp @@ -0,0 +1,489 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include + +using namespace Terrain; + +TerrainSystem::TerrainSystem() +{ + Terrain::TerrainSystemServiceRequestBus::Handler::BusConnect(); + AZ::TickBus::Handler::BusConnect(); + + m_currentSettings.m_systemActive = false; + m_currentSettings.m_worldBounds = AZ::Aabb::CreateNull(); + + m_requestedSettings = m_currentSettings; + m_requestedSettings.m_worldBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(4096.0f, 4096.0f, 2048.0f)); +} + +TerrainSystem::~TerrainSystem() +{ + AZ::TickBus::Handler::BusDisconnect(); + Terrain::TerrainSystemServiceRequestBus::Handler::BusDisconnect(); + + Deactivate(); +} + +void TerrainSystem::Activate() +{ + m_requestedSettings.m_systemActive = true; + m_terrainSettingsDirty = true; +} + +void TerrainSystem::Deactivate() +{ + m_requestedSettings.m_systemActive = false; + m_terrainSettingsDirty = true; +} + +void TerrainSystem::SetWorldMin(AZ::Vector3 worldOrigin) +{ + m_requestedSettings.m_worldBounds.SetMin(worldOrigin); + m_terrainSettingsDirty = true; +} + +void TerrainSystem::SetWorldMax(AZ::Vector3 worldBounds) +{ + m_requestedSettings.m_worldBounds.SetMax(worldBounds); + m_terrainSettingsDirty = true; +} + +void TerrainSystem::SetHeightQueryResolution(AZ::Vector2 queryResolution) +{ + m_requestedSettings.m_heightQueryResolution = queryResolution; + m_terrainSettingsDirty = true; +} + +void TerrainSystem::SetDebugWireframe(bool wireframeEnabled) +{ + m_requestedSettings.m_debugWireframeEnabled = wireframeEnabled; + m_terrainSettingsDirty = true; +} + + +AZ::Aabb TerrainSystem::GetTerrainAabb() const +{ + return m_currentSettings.m_worldBounds; +} + +AZ::Vector2 TerrainSystem::GetTerrainGridResolution() const +{ + return m_currentSettings.m_heightQueryResolution; +} + +float TerrainSystem::GetHeightSynchronous(float x, float y) const +{ + AZ::Vector3 inPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ()); + AZ::Vector3 outPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ()); + + AZStd::shared_lock lock(m_areaMutex); + + if (!m_registeredAreas.empty()) + { + for (auto& [areaId, areaBounds] : m_registeredAreas) + { + inPosition.SetZ(areaBounds.GetMin().GetZ()); + if (areaBounds.Contains(inPosition)) + { + Terrain::TerrainAreaHeightRequestBus::Event( + areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, + Terrain::TerrainAreaHeightRequestBus::Events::Sampler::DEFAULT); + } + } + } + + return AZ::GetClamp( + outPosition.GetZ(), m_currentSettings.m_worldBounds.GetMin().GetZ(), m_currentSettings.m_worldBounds.GetMax().GetZ()); +} + +float TerrainSystem::GetHeight(AZ::Vector3 position, [[maybe_unused]] Sampler sampler, [[maybe_unused]] bool* terrainExistsPtr) const +{ + if (terrainExistsPtr) + { + *terrainExistsPtr = true; + } + + return GetHeightSynchronous(position.GetX(), position.GetY()); +} + +float TerrainSystem::GetHeightFromFloats( + float x, float y, [[maybe_unused]] Sampler sampler, [[maybe_unused]] bool* terrainExistsPtr) const +{ + if (terrainExistsPtr) + { + *terrainExistsPtr = true; + } + + return GetHeightSynchronous(x, y); +} + +bool TerrainSystem::GetIsHoleFromFloats( + [[maybe_unused]] float x, [[maybe_unused]] float y, [[maybe_unused]] Sampler sampleFilter) const +{ + return false; +} + +AZ::Vector3 TerrainSystem::GetNormalSynchronous([[maybe_unused]] float x, [[maybe_unused]] float y) const +{ + return AZ::Vector3::CreateAxisZ(); +} + +AZ::Vector3 TerrainSystem::GetNormal( + AZ::Vector3 position, [[maybe_unused]] Sampler sampleFilter, [[maybe_unused]] bool* terrainExistsPtr) const +{ + if (terrainExistsPtr) + { + *terrainExistsPtr = true; + } + + return GetNormalSynchronous(position.GetX(), position.GetY()); +} + +AZ::Vector3 TerrainSystem::GetNormalFromFloats( + float x, float y, [[maybe_unused]] Sampler sampleFilter, [[maybe_unused]] bool* terrainExistsPtr) const +{ + if (terrainExistsPtr) + { + *terrainExistsPtr = true; + } + + return GetNormalSynchronous(x, y); +} + + +AzFramework::SurfaceData::SurfaceTagWeight TerrainSystem::GetMaxSurfaceWeight( + [[maybe_unused]] AZ::Vector3 position, [[maybe_unused]] Sampler sampleFilter, [[maybe_unused]] bool* terrainExistsPtr) const +{ + if (terrainExistsPtr) + { + *terrainExistsPtr = true; + } + + return AzFramework::SurfaceData::SurfaceTagWeight(); +} + +AzFramework::SurfaceData::SurfaceTagWeight TerrainSystem::GetMaxSurfaceWeightFromFloats( + [[maybe_unused]] float x, + [[maybe_unused]] float y, + [[maybe_unused]] Sampler sampleFilter, + [[maybe_unused]] bool* terrainExistsPtr) const +{ + if (terrainExistsPtr) + { + *terrainExistsPtr = true; + } + + return AzFramework::SurfaceData::SurfaceTagWeight(); +} + +const char* TerrainSystem::GetMaxSurfaceName( + [[maybe_unused]] AZ::Vector3 position, [[maybe_unused]] Sampler sampleFilter, [[maybe_unused]] bool* terrainExistsPtr) const +{ + if (terrainExistsPtr) + { + *terrainExistsPtr = true; + } + + return ""; +} + +/* +void TerrainSystem::GetSurfaceWeights( + [[maybe_unused]] const AZ::Vector3& inPosition, + [[maybe_unused]] Sampler sampleFilter, + [[maybe_unused]] SurfaceData::SurfaceTagWeightMap& outSurfaceWeights) +{ + // TODO: implement +} + +void TerrainSystem::GetSurfacePoint( + const AZ::Vector3& inPosition, [[maybe_unused]] Sampler sampleFilter, SurfaceData::SurfacePoint& outSurfacePoint) +{ + // TODO: Handle sampleFilter + + float sampleX = inPosition.GetX(); + float sampleY = inPosition.GetY(); + + GetHeight(inPosition, sampleFilter, outSurfacePoint.m_position); + //outSurfacePoint.m_position = AZ::Vector3(sampleX, sampleY, GetHeightSynchronous(sampleX, sampleY)); + outSurfacePoint.m_normal = GetNormalSynchronous(sampleX, sampleY); +} + + + + +void TerrainSystem::ProcessHeightsFromRegion(const AZ::Aabb& inRegion, const AZ::Vector2 stepSize, Sampler sampleFilter, SurfacePointRegionFillCallback perPositionCallback, TerrainDataReadyCallback onComplete) +{ + // Don't bother processing if we don't have a callback + if (!perPositionCallback) + { + return; + } + + uint32_t numSamplesX = static_cast((inRegion.GetMax().GetX() - inRegion.GetMin().GetX()) / stepSize.GetX()); + uint32_t numSamplesY = static_cast((inRegion.GetMax().GetY() - inRegion.GetMin().GetY()) / stepSize.GetY()); + + for (uint32_t y = 0; y < numSamplesY; y++) + { + for (uint32_t x = 0; x < numSamplesX; x++) + { + float fx = (float)(inRegion.GetMin().GetX() + (x * stepSize.GetX())); + float fy = (float)(inRegion.GetMin().GetY() + (y * stepSize.GetY())); + + SurfaceData::SurfacePoint surfacePoint; + GetHeight(AZ::Vector3(fx, fy, 0.0f), sampleFilter, surfacePoint.m_position); + perPositionCallback(surfacePoint, x, y); + } + } + + if (onComplete) + { + onComplete(); + } +} + + +void TerrainSystem::ProcessSurfacePointsFromRegion(const AZ::Aabb& inRegion, const AZ::Vector2 stepSize, Sampler sampleFilter, SurfacePointRegionFillCallback perPositionCallback, TerrainDataReadyCallback onComplete) +{ + // Don't bother processing if we don't have a callback + if (!perPositionCallback) + { + return; + } + + uint32_t numSamplesX = static_cast((inRegion.GetMax().GetX() - inRegion.GetMin().GetX()) / stepSize.GetX()); + uint32_t numSamplesY = static_cast((inRegion.GetMax().GetY() - inRegion.GetMin().GetY()) / stepSize.GetY()); + + for (uint32_t y = 0; y < numSamplesY; y++) + { + for (uint32_t x = 0; x < numSamplesX; x++) + { + float fx = (float)(inRegion.GetMin().GetX() + (x * stepSize.GetX())); + float fy = (float)(inRegion.GetMin().GetY() + (y * stepSize.GetY())); + + SurfaceData::SurfacePoint surfacePoint; + GetSurfacePoint(AZ::Vector3(fx, fy, inRegion.GetMin().GetZ()), sampleFilter, surfacePoint); + perPositionCallback(surfacePoint, x, y); + } + } + + if (onComplete) + { + onComplete(); + } +} +*/ + +void TerrainSystem::SystemActivate() +{ + { + AZStd::shared_lock lock(m_areaMutex); + m_registeredAreas.clear(); + } + + AzFramework::Terrain::TerrainDataRequestBus::Handler::BusConnect(); + + TerrainAreaRequestBus::Broadcast(&TerrainAreaRequestBus::Events::RegisterArea); +} + +void TerrainSystem::SystemDeactivate() +{ + AzFramework::Terrain::TerrainDataRequestBus::Handler::BusDisconnect(); + + { + AZStd::shared_lock lock(m_areaMutex); + m_registeredAreas.clear(); + } + + const AZ::RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get(); + auto terrainFeatureProcessor = scene->GetFeatureProcessor(); + if (terrainFeatureProcessor) + { + terrainFeatureProcessor->RemoveTerrainData(); + } +} + +void TerrainSystem::RegisterArea(AZ::EntityId areaId) +{ + { + AZStd::unique_lock lock(m_areaMutex); + AZ::Aabb aabb = AZ::Aabb::CreateNull(); + LmbrCentral::ShapeComponentRequestsBus::EventResult(aabb, areaId, &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); + m_registeredAreas[areaId] = aabb; + } + + RefreshArea(areaId); +} + +void TerrainSystem::UnregisterArea(AZ::EntityId areaId) +{ + { + AZStd::unique_lock lock(m_areaMutex); + AZ::Aabb aabb = AZ::Aabb::CreateNull(); + LmbrCentral::ShapeComponentRequestsBus::EventResult(aabb, areaId, &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); + m_registeredAreas.erase(areaId); + } + + RefreshArea(areaId); +} + +void TerrainSystem::RefreshArea(AZ::EntityId areaId) +{ + AZStd::unique_lock lock(m_areaMutex); + + auto areaAabb = m_registeredAreas.find(areaId); + + AZ::Aabb oldAabb = (areaAabb != m_registeredAreas.end()) ? areaAabb->second : AZ::Aabb::CreateNull(); + AZ::Aabb newAabb = AZ::Aabb::CreateNull(); + LmbrCentral::ShapeComponentRequestsBus::EventResult(newAabb, areaId, &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); + + m_registeredAreas[areaId] = newAabb; + + AZ::Aabb expandedAabb = oldAabb; + expandedAabb.AddAabb(newAabb); + + m_dirtyRegion.AddAabb(expandedAabb); + m_terrainHeightDirty = true; +} + +void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) +{ + bool terrainSettingsChanged = false; + + if (m_terrainSettingsDirty) + { + m_terrainSettingsDirty = false; + + // This needs to happen before the "system active" check below, because activating the system will cause the various + // terrain layer areas to request the current world bounds. + if (m_requestedSettings.m_worldBounds != m_currentSettings.m_worldBounds) + { + m_dirtyRegion = m_currentSettings.m_worldBounds; + m_dirtyRegion.AddAabb(m_requestedSettings.m_worldBounds); + m_terrainHeightDirty = true; + m_currentSettings.m_worldBounds = m_requestedSettings.m_worldBounds; + terrainSettingsChanged = true; + } + + if (m_requestedSettings.m_debugWireframeEnabled != m_currentSettings.m_debugWireframeEnabled) + { + m_dirtyRegion = AZ::Aabb::CreateNull(); + m_terrainHeightDirty = true; + terrainSettingsChanged = true; + } + + if (m_requestedSettings.m_heightQueryResolution != m_currentSettings.m_heightQueryResolution) + { + m_dirtyRegion = AZ::Aabb::CreateNull(); + m_terrainHeightDirty = true; + terrainSettingsChanged = true; + } + + if (m_requestedSettings.m_systemActive != m_currentSettings.m_systemActive) + { + m_requestedSettings.m_systemActive ? SystemActivate() : SystemDeactivate(); + + // Null dirty region will be interpreted as updating everything + m_dirtyRegion = AZ::Aabb::CreateNull(); + m_terrainHeightDirty = true; + terrainSettingsChanged = true; + } + + m_currentSettings = m_requestedSettings; + } + + if (m_currentSettings.m_systemActive && m_terrainHeightDirty) + { + AZStd::shared_lock lock(m_areaMutex); + + AZ::EntityId entityId(0); + AZ::Transform transform = AZ::Transform::CreateTranslation(m_currentSettings.m_worldBounds.GetCenter()); + + uint32_t width = aznumeric_cast( + (float)m_currentSettings.m_worldBounds.GetXExtent() / m_currentSettings.m_heightQueryResolution.GetX()); + uint32_t height = aznumeric_cast( + (float)m_currentSettings.m_worldBounds.GetYExtent() / m_currentSettings.m_heightQueryResolution.GetY()); + AZStd::vector pixels; + pixels.resize(width * height); + const uint32_t pixelDataSize = width * height * sizeof(float); + memset(pixels.data(), 0, pixelDataSize); + + for (auto& [areaId, areaBounds] : m_registeredAreas) + { + for (uint32_t y = 0; y < height; y++) + { + for (uint32_t x = 0; x < width; x++) + { + AZ::Vector3 inPosition( + (x * m_currentSettings.m_heightQueryResolution.GetX()) + m_currentSettings.m_worldBounds.GetMin().GetX(), + (y * m_currentSettings.m_heightQueryResolution.GetY()) + m_currentSettings.m_worldBounds.GetMin().GetY(), + areaBounds.GetMin().GetZ()); + if (areaBounds.Contains(inPosition)) + { + AZ::Vector3 outPosition; + const Terrain::TerrainAreaHeightRequests::Sampler sampleFilter = + Terrain::TerrainAreaHeightRequests::Sampler::DEFAULT; + + Terrain::TerrainAreaHeightRequestBus::Event( + areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, sampleFilter); + + pixels[(y * width) + x] = (outPosition.GetZ() - m_currentSettings.m_worldBounds.GetMin().GetZ()) / + m_currentSettings.m_worldBounds.GetExtents().GetZ(); + } + } + } + } + + const AZ::RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get(); + auto terrainFeatureProcessor = scene->GetFeatureProcessor(); + + AZ_Assert(terrainFeatureProcessor, "Unable to find a TerrainFeatureProcessor."); + if (terrainFeatureProcessor) + { + terrainFeatureProcessor->UpdateTerrainData( + entityId, transform, m_currentSettings.m_worldBounds, m_currentSettings.m_heightQueryResolution.GetX(), width, height, + pixels); + } + } + + if (terrainSettingsChanged || m_terrainHeightDirty) + { + AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask = + AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask::None; + + if (terrainSettingsChanged) + { + changeMask = static_cast( + changeMask | AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask::Settings); + } + if (m_terrainHeightDirty) + { + changeMask = static_cast( + changeMask | AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask::HeightData); + } + + // Make sure to set these *before* calling OnTerrainDataChanged, since it's possible that subsystems reacting to that call will + // cause the data to become dirty again. + AZ::Aabb dirtyRegion = m_dirtyRegion; + m_terrainHeightDirty = false; + m_dirtyRegion = AZ::Aabb::CreateNull(); + + AzFramework::Terrain::TerrainDataNotificationBus::Broadcast( + &AzFramework::Terrain::TerrainDataNotificationBus::Events::OnTerrainDataChanged, dirtyRegion, + changeMask); + } + +} diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h new file mode 100644 index 0000000000..56ed182047 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h @@ -0,0 +1,120 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace Terrain +{ + class TerrainSystem + : public AzFramework::Terrain::TerrainDataRequestBus::Handler + , private Terrain::TerrainSystemServiceRequestBus::Handler + , private AZ::TickBus::Handler + { + public: + TerrainSystem(); + ~TerrainSystem(); + + /////////////////////////////////////////// + // TerrainSystemServiceRequestBus::Handler Impl + + void SetWorldMin(AZ::Vector3 worldOrigin) override; + void SetWorldMax(AZ::Vector3 worldBounds) override; + void SetHeightQueryResolution(AZ::Vector2 queryResolution) override; + void SetDebugWireframe(bool wireframeEnabled) override; + + void Activate() override; + void Deactivate() override; + + void RegisterArea(AZ::EntityId areaId) override; + void UnregisterArea(AZ::EntityId areaId) override; + void RefreshArea(AZ::EntityId areaId) override; + + /////////////////////////////////////////// + // TerrainDataRequestBus::Handler Impl + AZ::Vector2 GetTerrainGridResolution() const override; + AZ::Aabb GetTerrainAabb() const override; + + //! Returns terrains height in meters at location x,y. + //! @terrainExistsPtr: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain + //! HOLE then *terrainExistsPtr will become false, + //! otherwise *terrainExistsPtr will become true. + float GetHeight(AZ::Vector3 position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + float GetHeightFromFloats(float x, float y, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + + //! Given an XY coordinate, return the max surface type and weight. + //! @terrainExists: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain + //! HOLE then *terrainExistsPtr will be set to false, + //! otherwise *terrainExistsPtr will be set to true. + AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeight( + AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromFloats( + float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + + //! Convenience function for low level systems that can't do a reverse lookup from Crc to string. Everyone else should use + //! GetMaxSurfaceWeight or GetMaxSurfaceWeightFromFloats. Not available in the behavior context. Returns nullptr if the position is + //! inside a hole or outside of the terrain boundaries. + const char* GetMaxSurfaceName( + AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + + //! Returns true if there's a hole at location x,y. + //! Also returns true if there's no terrain data at location x,y. + bool GetIsHoleFromFloats(float x, float y, Sampler sampleFilter = Sampler::BILINEAR) const override; + + // Given an XY coordinate, return the surface normal. + //! @terrainExists: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain + //! HOLE then *terrainExistsPtr will be set to false, + //! otherwise *terrainExistsPtr will be set to true. + AZ::Vector3 GetNormal( + AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + AZ::Vector3 GetNormalFromFloats( + float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override; + + private: + float GetHeightSynchronous(float x, float y) const; + AZ::Vector3 GetNormalSynchronous(float x, float y) const; + + // AZ::TickBus::Handler overrides ... + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + + void SystemActivate(); + void SystemDeactivate(); + + struct TerrainSystemSettings + { + AZ::Aabb m_worldBounds; + AZ::Vector2 m_heightQueryResolution{ 1.0f }; + bool m_debugWireframeEnabled{ false }; + bool m_systemActive{ false }; + }; + + TerrainSystemSettings m_currentSettings; + TerrainSystemSettings m_requestedSettings; + + bool m_terrainSettingsDirty = true; + bool m_terrainHeightDirty = false; + AZ::Aabb m_dirtyRegion; + + mutable AZStd::shared_mutex m_areaMutex; + AZStd::unordered_map m_registeredAreas; + }; +} // namespace Terrain diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystemBus.h b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystemBus.h new file mode 100644 index 0000000000..08521fd780 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystemBus.h @@ -0,0 +1,120 @@ +/* + * 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 +#include +#include +#include + +#include +#include + +namespace Terrain +{ + /** + * A bus to signal the life times of terrain areas + * Note: all the API are meant to be queued events + */ + class TerrainSystemServiceRequests + : public AZ::EBusTraits + { + public: + //////////////////////////////////////////////////////////////////////// + // EBusTraits + // singleton pattern + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + using MutexType = AZStd::recursive_mutex; + //////////////////////////////////////////////////////////////////////// + + virtual ~TerrainSystemServiceRequests() = default; + + virtual void Activate() = 0; + virtual void Deactivate() = 0; + + virtual void SetWorldMin(AZ::Vector3 worldOrigin) = 0; + virtual void SetWorldMax(AZ::Vector3 worldBounds) = 0; + virtual void SetHeightQueryResolution(AZ::Vector2 queryResolution) = 0; + virtual void SetDebugWireframe(bool wireframeEnabled) = 0; + + // register an area to override terrain + virtual void RegisterArea(AZ::EntityId areaId) = 0; + virtual void UnregisterArea(AZ::EntityId areaId) = 0; + virtual void RefreshArea(AZ::EntityId areaId) = 0; + }; + + using TerrainSystemServiceRequestBus = AZ::EBus; + + /** + * A bus to signal the life times of terrain areas + * Note: all the API are meant to be queued events + */ + class TerrainAreaRequests + : public AZ::ComponentBus + { + public: + //////////////////////////////////////////////////////////////////////// + // EBusTraits + using MutexType = AZStd::recursive_mutex; + //////////////////////////////////////////////////////////////////////// + + virtual ~TerrainAreaRequests() = default; + + virtual void RegisterArea() = 0; + virtual void RefreshArea() = 0; + + }; + + using TerrainAreaRequestBus = AZ::EBus; + + /** + * A bus to signal the life times of terrain areas + * Note: all the API are meant to be queued events + */ + class TerrainAreaHeightRequests + : public AZ::ComponentBus + { + public: + //////////////////////////////////////////////////////////////////////// + // EBusTraits + using MutexType = AZStd::recursive_mutex; + //////////////////////////////////////////////////////////////////////// + + virtual ~TerrainAreaHeightRequests() = default; + + enum class Sampler + { + BILINEAR, // Get the value at the requested location, using terrain sample grid to bilinear filter between sample grid points + CLAMP, // Clamp the input point to the terrain sample grid, then get the exact value + EXACT, // Directly get the value at the location, regardless of terrain sample grid density + + DEFAULT = BILINEAR + }; + + enum SurfacePointDataMask + { + POSITION = 0x01, + NORMAL = 0x02, + SURFACE_WEIGHTS = 0x04, + + DEFAULT = POSITION | NORMAL | SURFACE_WEIGHTS + }; + + // Synchronous single input location. The Vector3 input position versions are defined to ignore the input Z value. + + virtual void GetHeight(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, Sampler sampleFilter = Sampler::DEFAULT) = 0; + virtual void GetNormal(const AZ::Vector3& inPosition, AZ::Vector3& outNormal, Sampler sampleFilter = Sampler::DEFAULT) = 0; + //virtual void GetSurfaceWeights(const AZ::Vector3& inPosition, SurfaceTagWeightMap& outSurfaceWeights, Sampler sampleFilter = DEFAULT) = 0; + //virtual void GetSurfacePoint(const AZ::Vector3& inPosition, SurfacePoint& outSurfacePoint, SurfacePointDataMask dataMask = DEFAULT, Sampler sampleFilter = DEFAULT) = 0; + }; + + using TerrainAreaHeightRequestBus = AZ::EBus; + +} diff --git a/Gems/Terrain/Code/terrain_editor_shared_files.cmake b/Gems/Terrain/Code/terrain_editor_shared_files.cmake index 68ec9aeb54..9868f7d310 100644 --- a/Gems/Terrain/Code/terrain_editor_shared_files.cmake +++ b/Gems/Terrain/Code/terrain_editor_shared_files.cmake @@ -7,6 +7,14 @@ # set(FILES + Source/EditorComponents/EditorTerrainHeightGradientListComponent.cpp + Source/EditorComponents/EditorTerrainHeightGradientListComponent.h + Source/EditorComponents/EditorTerrainLayerSpawnerComponent.cpp + Source/EditorComponents/EditorTerrainLayerSpawnerComponent.h + Source/EditorComponents/EditorTerrainWorldComponent.cpp + Source/EditorComponents/EditorTerrainWorldComponent.h + Source/EditorComponents/EditorTerrainWorldDebuggerComponent.cpp + Source/EditorComponents/EditorTerrainWorldDebuggerComponent.h Source/EditorComponents/EditorTerrainSystemComponent.cpp Source/EditorComponents/EditorTerrainSystemComponent.h Source/EditorTerrainModule.cpp diff --git a/Gems/Terrain/Code/terrain_files.cmake b/Gems/Terrain/Code/terrain_files.cmake index c67d0c63b4..6a76dd7141 100644 --- a/Gems/Terrain/Code/terrain_files.cmake +++ b/Gems/Terrain/Code/terrain_files.cmake @@ -7,8 +7,21 @@ # set(FILES + Source/Components/TerrainHeightGradientListComponent.cpp + Source/Components/TerrainHeightGradientListComponent.h + Source/Components/TerrainLayerSpawnerComponent.cpp + Source/Components/TerrainLayerSpawnerComponent.h Source/Components/TerrainSurfaceDataSystemComponent.cpp Source/Components/TerrainSurfaceDataSystemComponent.h Source/Components/TerrainSystemComponent.cpp Source/Components/TerrainSystemComponent.h + Source/Components/TerrainWorldComponent.cpp + Source/Components/TerrainWorldComponent.h + Source/Components/TerrainWorldDebuggerComponent.cpp + Source/Components/TerrainWorldDebuggerComponent.h + Source/TerrainRenderer/TerrainFeatureProcessor.cpp + Source/TerrainRenderer/TerrainFeatureProcessor.h + Source/TerrainSystem/TerrainSystem.cpp + Source/TerrainSystem/TerrainSystem.h + Source/TerrainSystem/TerrainSystemBus.h ) diff --git a/Tools/RemoteConsole/ly_remote_console/ly_remote_console/remote_console_commands.py b/Tools/RemoteConsole/ly_remote_console/ly_remote_console/remote_console_commands.py index 872537bdca..559823c820 100755 --- a/Tools/RemoteConsole/ly_remote_console/ly_remote_console/remote_console_commands.py +++ b/Tools/RemoteConsole/ly_remote_console/ly_remote_console/remote_console_commands.py @@ -27,7 +27,8 @@ CONSOLE_MESSAGE_MAP = { 'COMMAND': BASE_MSG_TYPE + 5, 'AUTOCOMPLETELIST': BASE_MSG_TYPE + 6, 'AUTOCOMPLETELISTDONE': BASE_MSG_TYPE + 7, - 'GAMEPLAYEVENT': BASE_MSG_TYPE + 22 + 'GAMEPLAYEVENT': BASE_MSG_TYPE + 22, + 'CONNECTMESSAGE': BASE_MSG_TYPE + 25, } @@ -295,14 +296,7 @@ class RemoteConsole: self.handlers[key].set() continue - # The very first connection using the socket will return all of the auto complete items, turned off so no one - # wouldn't need to see them - elif message_type == CONSOLE_MESSAGE_MAP['AUTOCOMPLETELIST']: - pass - - # The after the autocompletelists finishes we will be ready to send console commands we determine that by - # looking at for an autocompletelistdone message - elif message_type == CONSOLE_MESSAGE_MAP['AUTOCOMPLETELISTDONE']: + elif message_type == CONSOLE_MESSAGE_MAP['CONNECTMESSAGE']: self.ready.set() # cleanup expect_log_line handers if the matching string was found or timeout happened. diff --git a/Tools/RemoteConsole/ly_remote_console/tests/unit/test_remote_console_commands.py b/Tools/RemoteConsole/ly_remote_console/tests/unit/test_remote_console_commands.py index 51a85f0413..9be0685874 100755 --- a/Tools/RemoteConsole/ly_remote_console/tests/unit/test_remote_console_commands.py +++ b/Tools/RemoteConsole/ly_remote_console/tests/unit/test_remote_console_commands.py @@ -141,11 +141,11 @@ class TestRemoteConsole(): @mock.patch('socket.socket', mock.MagicMock()) @mock.patch('ly_remote_console.remote_console_commands.threading', mock.MagicMock()) - def test_HandleMessage_AutoCompleteListDone_ReadySet(self): + def test_HandleMessage_ConnectMessage_ReadySet(self): rc_instance = remote_console.RemoteConsole() rc_instance.on_display = mock.MagicMock() rc_instance.ready = mock.MagicMock() - msg = b'70' # in python3 socket.recv returns byte array. 7 is AUTOCOMPLETELISTDONE + msg = b'I0' # in python3 socket.recv returns byte array. I is CONNECTMESSAGE rc_instance._handle_message(msg) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 6231f9006d..b2c06bc720 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -473,7 +473,18 @@ def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVar if(build_job.value.steps) { //this is a pipe with many steps so create all the build stages build_job.value.steps.each { build_step -> build_job_name = build_step - CreateBuildStage(pipelineConfig, platform.key, build_step, envVars).call() + envVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, platform.value.build_types[build_step].PIPELINE_ENV ?: EMPTY_JSON, pipelineName) + try { + CreateBuildStage(pipelineConfig, platform.key, build_step, envVars).call() + } + catch (Exception e) { + if (envVars['NONBLOCKING_STEP']?.toBoolean()) { + unstable(message: "Build step ${build_step} failed but it's a non-blocking step in build job ${build_job.key}") + } + else { + error "FAILURE: ${e}" + } + } } } else { CreateBuildStage(pipelineConfig, platform.key, build_job.key, envVars).call() diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 8928794a43..583a73b58b 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -213,10 +213,18 @@ "ASSET_PROCESSOR_PLATFORMS": "pc,server" } }, - "periodic_test_profile_vs2019": { + "periodic_test_profile_vs2019_pipe": { "TAGS": [ "nightly-incremental", - "nightly-clean", + "nightly-clean" + ], + "steps": [ + "awsi_deployment", + "periodic_test_profile_vs2019" + ] + }, + "periodic_test_profile_vs2019": { + "TAGS": [ "weekly-build-metrics" ], "COMMAND": "build_test_windows.cmd", @@ -378,7 +386,10 @@ ] }, "awsi_deployment": { - "TAGS": ["awsi-deployment"], + "TAGS": [], + "PIPELINE_ENV": { + "NONBLOCKING_STEP": "True" + }, "COMMAND": "deploy_cdk_applications.cmd", "PARAMETERS": {} } diff --git a/scripts/build/Platform/Windows/pipeline.json b/scripts/build/Platform/Windows/pipeline.json index 4c736d2292..6be55ec82d 100644 --- a/scripts/build/Platform/Windows/pipeline.json +++ b/scripts/build/Platform/Windows/pipeline.json @@ -18,7 +18,37 @@ } }, "PIPELINE_JENKINS_PARAMETERS": { - "awsi-deployment": [ + "nightly-incremental": [ + { + "parameter_name": "O3DE_AWS_PROJECT_NAME", + "parameter_type": "string", + "default_value": "", + "use_last_run_value": true, + "description": "The name of the O3DE project that stacks should be deployed for." + }, + { + "parameter_name": "O3DE_AWS_DEPLOY_REGION", + "parameter_type": "string", + "default_value": "", + "use_last_run_value": true, + "description": "The region to deploy the stacks into." + }, + { + "parameter_name": "ASSUME_ROLE_ARN", + "parameter_type": "string", + "default_value": "", + "use_last_run_value": true, + "description": "The ARN of the IAM role to assume to retrieve temporary AWS credentials." + }, + { + "parameter_name": "COMMIT_ID", + "parameter_type": "string", + "default_value": "", + "use_last_run_value": true, + "description": "The commit ID for locking the version of CDK applications to deploy." + } + ], + "nightly-clean": [ { "parameter_name": "O3DE_AWS_PROJECT_NAME", "parameter_type": "string",