diff --git a/AutomatedTesting/Gem/PythonTests/prefab/PrefabLevel_BasicWorkflow.py b/AutomatedTesting/Gem/PythonTests/prefab/PrefabLevel_BasicWorkflow.py index 44b7dc2ee4..7a600f7976 100644 --- a/AutomatedTesting/Gem/PythonTests/prefab/PrefabLevel_BasicWorkflow.py +++ b/AutomatedTesting/Gem/PythonTests/prefab/PrefabLevel_BasicWorkflow.py @@ -7,10 +7,14 @@ SPDX-License-Identifier: Apache-2.0 OR MIT # fmt:off class Tests(): - create_new_entity = ("Entity: 'CreateNewEntity' passed", "Entity: 'CreateNewEntity' failed") - create_prefab = ("Prefab: 'CreatePrefab' passed", "Prefab: 'CreatePrefab' failed") - instantiate_prefab = ("Prefab: 'InstantiatePrefab' passed", "Prefab: 'InstantiatePrefab' failed") - new_prefab_position = ("Prefab: new prefab's position is at the expected position", "Prefab: new prefab's position is *not* at the expected position") + create_new_entity = ("'CreateNewEntity' passed", "'CreateNewEntity' failed") + create_prefab = ("'CreatePrefab' passed", "'CreatePrefab' failed") + instantiate_prefab = ("'InstantiatePrefab' passed", "'InstantiatePrefab' failed") + has_one_child = ("instantiated prefab contains only one child as expected", "instantiated prefab does *not* contain only one child as expected") + instantiated_prefab_position = ("instantiated prefab's position is at the expected position", "instantiated prefab's position is *not* at the expected position") + delete_prefab = ("'DeleteEntitiesAndAllDescendantsInInstance' passed", "'DeleteEntitiesAndAllDescendantsInInstance' failed") + instantiated_prefab_removed = ("instantiated prefab's container entity has been removed", "instantiated prefab's container entity has *not* been removed") + instantiated_child_removed = ("instantiated prefab's child entity has been removed", "instantiated prefab's child entity has *not* been removed") # fmt:on def PrefabLevel_BasicWorkflow(): @@ -18,6 +22,7 @@ def PrefabLevel_BasicWorkflow(): This test will help verify if the following functions related to Prefab work as expected: - CreatePrefab - InstantiatePrefab + - DeleteEntitiesAndAllDescendantsInInstance """ import os @@ -35,31 +40,68 @@ def PrefabLevel_BasicWorkflow(): from azlmbr.math import Vector3 import azlmbr.legacy.general as general - EXPECTED_NEW_PREFAB_POSITION = Vector3(10.00, 20.0, 30.0) + NEW_PREFAB_NAME = "new_prefab" + NEW_PREFAB_FILE_NAME = NEW_PREFAB_NAME + ".prefab" + NEW_PREFAB_FILE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), NEW_PREFAB_FILE_NAME) + INSTANTIATED_PREFAB_POSITION = Vector3(10.00, 20.0, 30.0) + INSTANTIATED_PREFAB_NAME = "instantiated_prefab" + INSTANTIATED_CHILD_ENTITY_NAME = "child_1" + TEST_LEVEL_FOLDER = "Prefab" + TEST_LEVEL_NAME = "Base" + def find_entity_by_name(entity_name): + searchFilter = entity.SearchFilter() + searchFilter.names = [entity_name] + entityIds = entity.SearchBus(bus.Broadcast, 'SearchEntities', searchFilter) + if entityIds and entityIds[0].IsValid(): + return entityIds[0] + return None + + def print_error_if_failed(prefab_operation_result): + if not prefab_operation_result.IsSuccess(): + Report.info(f'Error message: {prefab_operation_result.GetError()}') + + +# Open the test level helper.init_idle() - helper.open_level("Prefab", "Base") + helper.open_level(TEST_LEVEL_FOLDER, TEST_LEVEL_NAME) # Create a new Entity at the root level new_entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', EntityId()) Report.result(Tests.create_new_entity, new_entity_id.IsValid()) # Checks for prefab creation passed or not - new_prefab_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'new_prefab.prefab') - create_prefab_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'CreatePrefabInMemory', [new_entity_id], new_prefab_file_path) - Report.result(Tests.create_prefab, create_prefab_result) + create_prefab_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'CreatePrefabInMemory', [new_entity_id], NEW_PREFAB_FILE_PATH) + Report.result(Tests.create_prefab, create_prefab_result.IsSuccess()) + print_error_if_failed(create_prefab_result) # Checks for prefab instantiation passed or not - container_entity_id = prefab.PrefabPublicRequestBus(bus.Broadcast, 'InstantiatePrefab', new_prefab_file_path, EntityId(), EXPECTED_NEW_PREFAB_POSITION) - Report.result(Tests.instantiate_prefab, container_entity_id.IsValid()) + instantiate_prefab_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'InstantiatePrefab', NEW_PREFAB_FILE_PATH, EntityId(), INSTANTIATED_PREFAB_POSITION) + Report.result(Tests.instantiate_prefab, instantiate_prefab_result.IsSuccess() and instantiate_prefab_result.GetValue().IsValid()) + print_error_if_failed(instantiate_prefab_result) + + container_entity_id = instantiate_prefab_result.GetValue() + editor.EditorEntityAPIBus(bus.Event, 'SetName', container_entity_id, INSTANTIATED_PREFAB_NAME) + + children_entity_ids = editor.EditorEntityInfoRequestBus(bus.Event, 'GetChildren', container_entity_id) + Report.result(Tests.has_one_child, len(children_entity_ids) is 1) + + child_entity_id = children_entity_ids[0] + editor.EditorEntityAPIBus(bus.Event, 'SetName', child_entity_id, INSTANTIATED_CHILD_ENTITY_NAME) # Checks if the new prefab is at the correct position and if it fails, it will provide the expected postion and the actual postion of the entity in the Editor log - new_prefab_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", container_entity_id) - is_at_position = new_prefab_position.IsClose(EXPECTED_NEW_PREFAB_POSITION) - Report.result(Tests.new_prefab_position, is_at_position) + actual_prefab_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", container_entity_id) + is_at_position = actual_prefab_position.IsClose(INSTANTIATED_PREFAB_POSITION) + Report.result(Tests.instantiated_prefab_position, is_at_position) if not is_at_position: - Report.info(f'Expected position: {EXPECTED_NEW_PREFAB_POSITION.ToString()}, actual position: {new_prefab_position.ToString()}') - + Report.info(f'Expected position: {INSTANTIATED_PREFAB_POSITION.ToString()}, actual position: {actual_prefab_position.ToString()}') + +# Checks for prefab deletion passed or not + delete_prefab_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'DeleteEntitiesAndAllDescendantsInInstance', [container_entity_id]) + Report.result(Tests.delete_prefab, delete_prefab_result.IsSuccess()) + print_error_if_failed(delete_prefab_result) + Report.result(Tests.instantiated_prefab_removed, find_entity_by_name(INSTANTIATED_PREFAB_NAME) is None) + Report.result(Tests.instantiated_child_removed, find_entity_by_name(INSTANTIATED_CHILD_ENTITY_NAME) is None) if __name__ == "__main__": from editor_python_test_tools.utils import Report diff --git a/Code/Editor/Lib/Tests/test_ViewportManipulatorController.cpp b/Code/Editor/Lib/Tests/test_ViewportManipulatorController.cpp index a2a7617083..9dc7e65cef 100644 --- a/Code/Editor/Lib/Tests/test_ViewportManipulatorController.cpp +++ b/Code/Editor/Lib/Tests/test_ViewportManipulatorController.cpp @@ -77,7 +77,7 @@ namespace UnitTest class ViewportManipulatorControllerFixture : public AllocatorsTestFixture { public: - static const AzFramework::ViewportId TestViewportId = AzFramework::ViewportId(0); + static const AzFramework::ViewportId TestViewportId; void SetUp() override { @@ -108,6 +108,8 @@ namespace UnitTest AZStd::unique_ptr m_inputChannelMapper; }; + const AzFramework::ViewportId ViewportManipulatorControllerFixture::TestViewportId = AzFramework::ViewportId(0); + TEST_F(ViewportManipulatorControllerFixture, An_event_is_not_propagated_to_the_viewport_when_a_manipulator_handles_it_first) { // forward input events to our controller list diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp index b10fe35513..6b60f96089 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp @@ -646,16 +646,27 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con QAction* action = nullptr; - action = menu->addAction(QObject::tr("Create entity")); - QObject::connect(action, &QAction::triggered, action, [this] { ContextMenu_NewEntity(); }); - - if (selected.size() == 1) + // when nothing is selected, entity is created at root level + if (selected.size() == 0) { - action = menu->addAction(QObject::tr("Create child entity")); - QObject::connect(action, &QAction::triggered, action, [selected] - { - EBUS_EVENT(AzToolsFramework::EditorRequests::Bus, CreateNewEntityAsChild, selected.front()); - }); + action = menu->addAction(QObject::tr("Create entity")); + QObject::connect( + action, &QAction::triggered, action, + [this] + { + ContextMenu_NewEntity(); + }); + } + // when a single entity is selected, entity is created as its child + else if (selected.size() == 1) + { + action = menu->addAction(QObject::tr("Create entity")); + QObject::connect( + action, &QAction::triggered, action, + [selected] + { + EBUS_EVENT(AzToolsFramework::EditorRequests::Bus, CreateNewEntityAsChild, selected.front()); + }); } bool prefabSystemEnabled = false; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 7af953efca..e618a11344 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -61,7 +61,7 @@ namespace AzToolsFramework m_prefabUndoCache.Destroy(); } - PrefabOperationResult PrefabPublicHandler::CreatePrefabInMemory(const AZStd::vector& entityIds, AZ::IO::PathView filePath) + PrefabOperationResult PrefabPublicHandler::CreatePrefabInMemory(const EntityIdList& entityIds, AZ::IO::PathView filePath) { EntityList inputEntityList, topLevelEntities; AZ::EntityId commonRootEntityId; @@ -264,7 +264,7 @@ namespace AzToolsFramework return AZ::Success(); } - PrefabOperationResult PrefabPublicHandler::CreatePrefabInDisk(const AZStd::vector& entityIds, AZ::IO::PathView filePath) + PrefabOperationResult PrefabPublicHandler::CreatePrefabInDisk(const EntityIdList& entityIds, AZ::IO::PathView filePath) { auto result = CreatePrefabInMemory(entityIds, filePath); if (result.IsSuccess()) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 8f124e8edd..6faaa6932f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -43,9 +43,9 @@ namespace AzToolsFramework // PrefabPublicInterface... PrefabOperationResult CreatePrefabInDisk( - const AZStd::vector& entityIds, AZ::IO::PathView filePath) override; + const EntityIdList& entityIds, AZ::IO::PathView filePath) override; PrefabOperationResult CreatePrefabInMemory( - const AZStd::vector& entityIds, AZ::IO::PathView filePath) override; + const EntityIdList& entityIds, AZ::IO::PathView filePath) override; InstantiatePrefabResult InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position) override; PrefabOperationResult SavePrefab(AZ::IO::Path filePath) override; PrefabEntityResult CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h index 67d65dfca6..83763ef55f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h @@ -47,7 +47,7 @@ namespace AzToolsFramework * @return An outcome object; on failure, it comes with an error message detailing the cause of the error. */ virtual PrefabOperationResult CreatePrefabInDisk( - const AZStd::vector& entityIds, AZ::IO::PathView filePath) = 0; + const EntityIdList& entityIds, AZ::IO::PathView filePath) = 0; /** * Create a prefab out of the entities provided, at the path provided, and keep it in memory. @@ -57,7 +57,7 @@ namespace AzToolsFramework * @return An outcome object; on failure, it comes with an error message detailing the cause of the error. */ virtual PrefabOperationResult CreatePrefabInMemory( - const AZStd::vector& entityIds, AZ::IO::PathView filePath) = 0; + const EntityIdList& entityIds, AZ::IO::PathView filePath) = 0; /** * Instantiate a prefab from a prefab file. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestBus.h index 1b86d3cd4e..1605ad97be 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestBus.h @@ -11,13 +11,20 @@ #include #include #include +#include #include +#include #include namespace AzToolsFramework { + using EntityIdList = AZStd::vector; + namespace Prefab { + using PrefabOperationResult = AZ::Outcome; + using InstantiatePrefabResult = AZ::Outcome; + /** * The primary purpose of this bus is to facilitate writing automated tests for prefabs. * It calls PrefabPublicInterface internally to talk to the prefab system. @@ -40,14 +47,25 @@ namespace AzToolsFramework /** * Create a prefab out of the entities provided, at the path provided, and keep it in memory. * Automatically detects descendants of entities, and discerns between entities and child instances. + * Return whether the creation succeeded or not. */ - virtual bool CreatePrefabInMemory( - const AZStd::vector& entityIds, AZStd::string_view filePath) = 0; + virtual PrefabOperationResult CreatePrefabInMemory( + const EntityIdList& entityIds, AZStd::string_view filePath) = 0; /** * Instantiate a prefab from a prefab file. + * Return the container entity id of the prefab instantiated if instantiation succeeded. */ - virtual AZ::EntityId InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position) = 0; + virtual InstantiatePrefabResult InstantiatePrefab( + AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position) = 0; + + /** + * Deletes all entities and their descendants from the owning instance. Bails if the entities don't + * all belong to the same instance. + * Return whether the deletion succeeded or not. + */ + virtual PrefabOperationResult DeleteEntitiesAndAllDescendantsInInstance(const EntityIdList& entityIds) = 0; + }; using PrefabPublicRequestBus = AZ::EBus; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.cpp index 0e68a286a6..d964be25af 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.cpp @@ -25,6 +25,7 @@ namespace AzToolsFramework ->Attribute(AZ::Script::Attributes::Module, "prefab") ->Event("CreatePrefabInMemory", &PrefabPublicRequests::CreatePrefabInMemory) ->Event("InstantiatePrefab", &PrefabPublicRequests::InstantiatePrefab) + ->Event("DeleteEntitiesAndAllDescendantsInInstance", &PrefabPublicRequests::DeleteEntitiesAndAllDescendantsInInstance) ; } } @@ -44,36 +45,20 @@ namespace AzToolsFramework m_prefabPublicInterface = nullptr; } - bool PrefabPublicRequestHandler::CreatePrefabInMemory(const AZStd::vector& entityIds, AZStd::string_view filePath) + PrefabOperationResult PrefabPublicRequestHandler::CreatePrefabInMemory(const EntityIdList& entityIds, AZStd::string_view filePath) { - auto createPrefabOutcome = m_prefabPublicInterface->CreatePrefabInMemory(entityIds, filePath); - if (!createPrefabOutcome.IsSuccess()) - { - AZ_Error("CreatePrefabInMemory", false, - "Failed to create Prefab on file path '%.*s'. Error message: %s.", - AZ_STRING_ARG(filePath), - createPrefabOutcome.GetError().c_str()); - - return false; - } - - return true; + return m_prefabPublicInterface->CreatePrefabInMemory(entityIds, filePath); } - AZ::EntityId PrefabPublicRequestHandler::InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position) + InstantiatePrefabResult PrefabPublicRequestHandler::InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position) { - auto instantiatePrefabOutcome = m_prefabPublicInterface->InstantiatePrefab(filePath, parent, position); - if (!instantiatePrefabOutcome.IsSuccess()) - { - AZ_Error("InstantiatePrefab", false, - "Failed to instantiate Prefab on file path '%.*s'. Error message: %s.", - AZ_STRING_ARG(filePath), - instantiatePrefabOutcome.GetError().c_str()); - - return AZ::EntityId(); - } - - return instantiatePrefabOutcome.GetValue(); + return m_prefabPublicInterface->InstantiatePrefab(filePath, parent, position); } + + PrefabOperationResult PrefabPublicRequestHandler::DeleteEntitiesAndAllDescendantsInInstance(const EntityIdList& entityIds) + { + return m_prefabPublicInterface->DeleteEntitiesAndAllDescendantsInInstance(entityIds); + } + } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.h index 548bc8e04a..87608e1263 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.h @@ -31,8 +31,9 @@ namespace AzToolsFramework void Connect(); void Disconnect(); - bool CreatePrefabInMemory(const AZStd::vector& entityIds, AZStd::string_view filePath) override; - AZ::EntityId InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position) override; + PrefabOperationResult CreatePrefabInMemory(const EntityIdList& entityIds, AZStd::string_view filePath) override; + InstantiatePrefabResult InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position) override; + PrefabOperationResult DeleteEntitiesAndAllDescendantsInInstance(const EntityIdList& entityIds) override; private: PrefabPublicInterface* m_prefabPublicInterface = nullptr; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index eb9cf2b65f..9f75abb32a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -363,12 +364,25 @@ namespace AzToolsFramework if (hasUserSelectedValidSourceFile) { - // Get position (center of viewport). If no viewport is available, (0,0,0) will be used. - AZ::Vector3 viewportCenterPosition = AZ::Vector3::CreateZero(); - EditorRequestBus::BroadcastResult(viewportCenterPosition, &EditorRequestBus::Events::GetWorldPositionAtViewportCenter); + AZ::EntityId parentId; + AZ::Vector3 position = AZ::Vector3::CreateZero(); + + EntityIdList selectedEntities; + ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequests::GetSelectedEntities); + // if one entity is selected, instantiate prefab as its child and place it at same position as parent + if (selectedEntities.size() == 1) + { + parentId = selectedEntities.front(); + AZ::TransformBus::EventResult(position, parentId, &AZ::TransformInterface::GetWorldTranslation); + } + // otherwise instantiate it at root level and center of viewport + else + { + EditorRequestBus::BroadcastResult(position, &EditorRequestBus::Events::GetWorldPositionAtViewportCenter); + } // Instantiating from context menu always puts the instance at the root level - auto createPrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(prefabFilePath, AZ::EntityId(), viewportCenterPosition); + auto createPrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(prefabFilePath, parentId, position); if (!createPrefabOutcome.IsSuccess()) { diff --git a/Code/Framework/CMakeLists.txt b/Code/Framework/CMakeLists.txt index 61f65de5a4..8cc02fd4e8 100644 --- a/Code/Framework/CMakeLists.txt +++ b/Code/Framework/CMakeLists.txt @@ -15,6 +15,5 @@ add_subdirectory(AzTest) add_subdirectory(AzToolsFramework) add_subdirectory(AzManipulatorTestFramework) add_subdirectory(AzNetworking) -add_subdirectory(Crcfix) add_subdirectory(GFxFramework) add_subdirectory(GridMate) diff --git a/Code/Framework/Crcfix/CMakeLists.txt b/Code/Framework/Crcfix/CMakeLists.txt deleted file mode 100644 index 4fdad1e338..0000000000 --- a/Code/Framework/Crcfix/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -# -# 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 -# -# - -if (NOT PAL_TRAIT_BUILD_HOST_TOOLS) - return() -endif() - -include(Platform/${PAL_PLATFORM_NAME}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) -if (NOT PAL_TRAIT_BUILD_CRCFIX) - return() -endif() - -ly_add_target( - NAME Crcfix EXECUTABLE - NAMESPACE AZ - FILES_CMAKE - crcfix_files.cmake - BUILD_DEPENDENCIES - PRIVATE - AZ::AzCore -) - -ly_add_source_properties( - SOURCES crcfix.cpp - PROPERTY COMPILE_DEFINITIONS - VALUES _CRT_SECURE_NO_WARNINGS -) diff --git a/Code/Framework/Crcfix/Platform/Linux/PAL_linux.cmake b/Code/Framework/Crcfix/Platform/Linux/PAL_linux.cmake deleted file mode 100644 index a63f2bed45..0000000000 --- a/Code/Framework/Crcfix/Platform/Linux/PAL_linux.cmake +++ /dev/null @@ -1,9 +0,0 @@ -# -# 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 -# -# - -set(PAL_TRAIT_BUILD_CRCFIX FALSE) diff --git a/Code/Framework/Crcfix/Platform/Mac/PAL_mac.cmake b/Code/Framework/Crcfix/Platform/Mac/PAL_mac.cmake deleted file mode 100644 index a63f2bed45..0000000000 --- a/Code/Framework/Crcfix/Platform/Mac/PAL_mac.cmake +++ /dev/null @@ -1,9 +0,0 @@ -# -# 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 -# -# - -set(PAL_TRAIT_BUILD_CRCFIX FALSE) diff --git a/Code/Framework/Crcfix/Platform/Windows/PAL_windows.cmake b/Code/Framework/Crcfix/Platform/Windows/PAL_windows.cmake deleted file mode 100644 index 8a8884139d..0000000000 --- a/Code/Framework/Crcfix/Platform/Windows/PAL_windows.cmake +++ /dev/null @@ -1,9 +0,0 @@ -# -# 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 -# -# - -set(PAL_TRAIT_BUILD_CRCFIX TRUE) diff --git a/Code/Framework/Crcfix/crcfix.cpp b/Code/Framework/Crcfix/crcfix.cpp deleted file mode 100644 index 778b9e2185..0000000000 --- a/Code/Framework/Crcfix/crcfix.cpp +++ /dev/null @@ -1,538 +0,0 @@ -/* - * 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 - -int g_totalFixTimeMs = 0; -int g_longestFixTimeMs = 0; - -class Filename -{ - wchar_t fullpath[MAX_PATH]; - wchar_t drive[_MAX_DRIVE]; - wchar_t dir[_MAX_DIR]; - wchar_t fname[_MAX_FNAME]; - wchar_t ext[_MAX_EXT]; - -public: - Filename() - { - fullpath[0] = drive[0] = dir[0] = fname[0] = ext[0] = 0; - } - - Filename(const AZStd::wstring& filename) - { - _wsplitpath(filename.c_str(), drive, dir, fname, ext); - wcscpy(fullpath, filename.c_str()); - } - - void SetExt(const wchar_t* pExt) { wcscpy(ext, pExt); _wmakepath(fullpath, drive, dir, fname, pExt); } - const wchar_t* GetFullPath() const { return fullpath; } - bool Exists() const { return _waccess(fullpath, 0) == 0; } - bool IsReadOnly() const { return _waccess(fullpath, 6) == -1; } - bool SetReadOnly() const { return _wchmod(fullpath, _S_IREAD) == 0; } - bool SetWritable() const { return _wchmod(fullpath, _S_IREAD | _S_IWRITE) == 0; } - bool Delete() const { return _wremove(fullpath) == 0; } - bool Rename(const wchar_t* fn2) const{ return MoveFileEx(fullpath, fn2, MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING) == 0; } - bool Copy(const wchar_t* dest) const { return ::CopyFile(fullpath, dest, FALSE) == TRUE; } -}; - -class CRCfix -{ - int lastchar; - int linenum; - -public: - void SkipToEOL(FILE* infile); - char* GetToken(FILE* infile, FILE* outfile); - void GetPreviousCRC(char* token, FILE* infile); - int Fix(Filename srce); -}; - -void FixFiles(const AZStd::wstring& dir, const AZStd::wstring& files, FILETIME* pLastRun, bool verbose, int& nFound, int& nProcessed, int& nFixed, int& nFailed) -{ - CRCfix fixer; - WIN32_FIND_DATA wfd; - HANDLE hFind; - hFind = FindFirstFile((dir + files).c_str(), &wfd); - if (hFind != INVALID_HANDLE_VALUE) - { - do - { - if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) - { - if (verbose) - { - AZ_TracePrintf("CrcFix", "\tProcessing %ls ...", wfd.cFileName); - } - nFound++; - - int n = 0; - if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0 && (!pLastRun || CompareFileTime(pLastRun, &wfd.ftLastWriteTime) <= 0)) - { - n = fixer.Fix(Filename(dir + L"\\" + wfd.cFileName)); - nProcessed++; - } - if (n < 0) - { - nFailed++; - if (verbose) - { - AZ_TracePrintf("CrcFix", "Failed\n"); - } - } - else - { - if (verbose) - { - AZ_TracePrintf("CrcFix", n > 0 ? "Done\n" : (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0 ? "ReadOnly\n" : "Unchanged\n"); - } - nFixed += n; - } - } - } while (FindNextFile(hFind, &wfd)); - } - FindClose(hFind); -} - -void FixDirectories(const AZStd::wstring& dirs, const AZStd::wstring& files, FILETIME* pLastRun, bool verbose, int& nFound, int& nProcessed, int& nFixed, int& nFailed) -{ - if (verbose) - { - AZ_TracePrintf("CrcFix", "Processing %ls ...\n", dirs.c_str()); - } - - // do files - FixFiles(dirs, files, pLastRun, verbose, nFound, nProcessed, nFixed, nFailed); - - // do folders - WIN32_FIND_DATA wfd; - HANDLE hFind; - hFind = FindFirstFile((dirs + L"\\*").c_str(), &wfd); - if (hFind != INVALID_HANDLE_VALUE) - { - do - { - if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) - { - if (wfd.cFileName[0] == '.') - { - continue; - } - FixDirectories(AZStd::wstring(dirs + L"\\" + wfd.cFileName), files, pLastRun, verbose, nFound, nProcessed, nFixed, nFailed); - } - } while (FindNextFile(hFind, &wfd)); - } - FindClose(hFind); -} - -int main(int argc, char* argv[]) -{ - AZStd::chrono::system_clock::time_point startTime = AZStd::chrono::system_clock::now(); - - AZ::SystemAllocator::Descriptor desc; - //desc.m_stackRecordLevels = 15; - AZ::AllocatorInstance::Create(desc); - //if (AZ::AllocatorInstance::Get().GetRecords()) { - // AZ::AllocatorInstance::Get().GetRecords()->SetMode(AZ::Debug::AllocationRecords::RECORD_FULL); - //} - { - if (argc < 2) - { - AZ_TracePrintf("CrcFix", "Usage:\n crcfix [-v(erbose)] [-log:logfile] {path[\\*][\\*.*]}\n"); - AZ_TracePrintf("CrcFix", "\n Ex:\n crcfix -v -log:timestamp.log src\\*\\*.cpp src\\*\\*.h ..\\scripts\\*.*\n\n"); - } - - char root[MAX_PATH]; - AZ::Utils::GetExecutableDirectory(root, MAX_PATH); - - AZStd::vector entries; - - AZStd::wstring logfilename; - FILETIME lastRun; - FILETIME* pLastRun = NULL; - - bool verbose = false; - - for (int iArg = 1; iArg < argc; ++iArg) - { - const char* pArg = argv[iArg]; - if (!pArg) - { - continue; - } - AZStd::wstring pArgW; - AZStd::to_wstring(pArgW, pArg); - if (_strnicmp(pArg, "-log:", 5) == 0) - { - logfilename.assign(pArgW.begin() + 5, pArgW.end()); - HANDLE hFile = CreateFile(logfilename.data(), 0, 0, NULL, OPEN_EXISTING, 0, NULL); - if (hFile != INVALID_HANDLE_VALUE) - { - pLastRun = &lastRun; - GetFileTime(hFile, NULL, NULL, pLastRun); - CloseHandle(hFile); - } - } - else if (_stricmp(pArg, "-v") == 0) - { - verbose = true; - } - else - { - entries.emplace_back(AZStd::move(pArgW)); - } - } - - // for each entry from the command line... - int nFound = 0; - int nProcessed = 0; - int nFixed = 0; - int nFailed = 0; - for (AZStd::vector::const_iterator iEntry = entries.begin(); iEntry != entries.end(); ++iEntry) - { - AZStd::wstring entry = (iEntry->at(0) == L'\\' || iEntry->find(L":") != iEntry->npos) ? *iEntry : AZStd::wstring(root) + L"\\" + *iEntry; - AZStd::wstring::size_type split = entry.find(L"*\\"); - bool doSubdirs = split != entry.npos; - if (doSubdirs) - { - FixDirectories(entry.substr(0, split), entry.substr(split + 1), pLastRun, verbose, nFound, nProcessed, nFixed, nFailed); - } - else - { - split = entry.rfind(L"\\"); - if (split == entry.npos) - { - split = 0; - } - FixFiles(entry.substr(0, split), entry.substr(split), pLastRun, verbose, nFound, nProcessed, nFixed, nFailed); - } - } - - // update timestamp - if (!logfilename.empty()) - { - HANDLE hFile = CreateFile(logfilename.data(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); - GetSystemTimeAsFileTime(&lastRun); - SetFileTime(hFile, NULL, NULL, &lastRun); - char log[1024]; - DWORD oCount; - - sprintf(log, "Batches processed: %zu\n\tFiles found: %d\n\tFiles processed: %d\n\tFiles fixed: %d\n\tFiles failed: %d\n", entries.size(), nFound, nProcessed, nFixed, nFailed); - WriteFile(hFile, log, static_cast(strlen(log)), &oCount, NULL); - - AZStd::chrono::system_clock::time_point endTime = AZStd::chrono::system_clock::now(); - sprintf(log, "Total running time: %.2f secs.\n\tTotal processing time: %.2f secs.\n\tLongest processing time: %.2f secs.\n", (float)AZStd::chrono::milliseconds(endTime - startTime).count() / 1000.f, (float)g_totalFixTimeMs / 1000.f, (float)g_longestFixTimeMs / 1000.f); - WriteFile(hFile, log, static_cast(strlen(log)), &oCount, NULL); - - CloseHandle(hFile); - } - } - - AZ::AllocatorInstance::Destroy(); - return 0; -} - -//----------------------------------------------------------------------------- -// CRCfix -//----------------------------------------------------------------------------- -void CRCfix::SkipToEOL(FILE* infile) -{ - int c; - for (c = lastchar; c != EOF && c != '\n'; c = fgetc(infile)) - { - ; - } - lastchar = fgetc(infile); - linenum++; -} -//----------------------------------------------------------------------------- -char* CRCfix::GetToken(FILE* infile, FILE* outfile) -{ - static char token[512]; - bool commentline = false; - bool commentblock = false; - bool doublequote = false; - bool singlequote = false; - int i = 0; - int c; - - if (lastchar == EOF) - { - return NULL; - } - - for (c = lastchar; c != EOF; c = fgetc(infile)) - { - if (!commentline && !commentblock && !doublequote && !singlequote) - { - if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '#' || c == '_') - { - token[i++] = c; - continue; - } - else - { - if (i) - { - lastchar = c; - token[i] = 0; - i = 0; - return token; - } - } - } - - if (commentline) - { - if (c == '\n') - { - commentline = false; - } - } - else if (commentblock) - { - while (c == '*') - { - c = fgetc(infile); - if (c == '/') - { - commentblock = false; - } - fputc('*', outfile); - } - } - - if (!commentline && !commentblock) - { - if (c == '"' && !singlequote) - { - doublequote = !doublequote; - } - else if (c == '\'' && !doublequote) - { - singlequote = !singlequote; - } - else if (!singlequote && !doublequote) - { - if (c == '/') - { - c = fgetc(infile); - if (c == '/') - { - commentline = true; - } - else if (c == '*') - { - commentblock = true; - } - if (c == '\'') - { - singlequote = true; - } - else if (c == '"') - { - doublequote = true; - } - fputc('/', outfile); - } - } - else if (c == '\\') - { - fputc(c, outfile); - c = fgetc(infile); - } - } - fputc(c, outfile); - if (c == '\n') - { - linenum++; - } - } - lastchar = c; - token[i] = 0; - return i ? token : NULL; -} -//----------------------------------------------------------------------------- -void CRCfix::GetPreviousCRC(char* token, FILE* infile) -{ - int c; - while ((c = fgetc(infile)) != ')') - { - *token++ = c; - } - *token = 0; -} -//----------------------------------------------------------------------------- -int CRCfix::Fix(Filename srce) -{ - AZStd::chrono::system_clock::time_point startTime = AZStd::chrono::system_clock::now(); - - bool changed = false; - Filename dest(srce); - dest.SetExt(L"xxx"); - - linenum = 0; - - FILE* infile = _wfopen(srce.GetFullPath(), L"r"); - FILE* outfile = _wfopen(dest.GetFullPath(), L"w"); - - if (!infile || !outfile) - { - if (infile) - { - fclose(infile); - infile = nullptr; - } - - if (outfile) - { - fclose(outfile); - outfile = nullptr; - } - - int dt = static_cast(AZStd::chrono::milliseconds(AZStd::chrono::system_clock::now() - startTime).count()); - g_totalFixTimeMs += dt; - if (dt > g_longestFixTimeMs) - { - g_longestFixTimeMs = dt; - } - - return -1; - } - - lastchar = fgetc(infile); - - while (char* token = GetToken(infile, outfile)) - { - bool got = false; - - if (strcmp(token, "AZ_CRC") == 0 && lastchar == '(') - { - size_t i = strlen(token); - token[i++] = lastchar; - int c = fgetc(infile); - - if (c == '"') - { - size_t j = i + 1; - - do - { - token[i++] = c; - c = fgetc(infile); - } while (c != '"'); - - token[i++] = c; - c = fgetc(infile); - - int oldcrc = 0, newcrc; - - if (c == ',') - { - GetPreviousCRC(token + i, infile); - sscanf(token + i, "%i", &oldcrc); - c = ')'; - } - - if (c == ')') - { - token[i] = 0; - c = fgetc(infile); - got = true; - newcrc = AZ::Crc32(token + j, i - j - 1, true); - fprintf(outfile, "%s, 0x%08x)", token, newcrc); - if (newcrc != oldcrc) - { - changed = true; - } - } - } - lastchar = c; - token[i] = 0; - } - if (!got) - { - fwrite(token, 1, strlen(token), outfile); - } - } - fclose(infile); - fclose(outfile); - - if (changed) - { - Filename backup(srce); - backup.SetExt(L"crcfix_old"); - - if (backup.Exists()) - { - backup.SetWritable(); - [[maybe_unused]] bool deleted = backup.Delete(); - AZ_Assert(deleted, "failed to delete"); - } - - if (!srce.Copy(backup.GetFullPath())) - { - AZ_TracePrintf("CrcFix", "Failed to copy %ls to %ls\n", srce, backup); - - int dt = static_cast(AZStd::chrono::milliseconds(AZStd::chrono::system_clock::now() - startTime).count()); - g_totalFixTimeMs += dt; - if (dt > g_longestFixTimeMs) - { - g_longestFixTimeMs = dt; - } - - return -1; - } - - if (!dest.Rename(srce.GetFullPath())) - { - AZ_TracePrintf("CrcFix", "Failed to rename %ls to %ls\n", dest, srce); - - int dt = static_cast(AZStd::chrono::milliseconds(AZStd::chrono::system_clock::now() - startTime).count()); - g_totalFixTimeMs += dt; - if (dt > g_longestFixTimeMs) - { - g_longestFixTimeMs = dt; - } - - return -1; - } - - if (!backup.Delete()) - { - AZ_TracePrintf("CrcFix", "Failed to delete %ls\n", backup); - } - } - else - { - dest.Delete(); - } - - - int dt = static_cast(AZStd::chrono::milliseconds(AZStd::chrono::system_clock::now() - startTime).count()); - g_totalFixTimeMs += dt; - if (dt > g_longestFixTimeMs) - { - g_longestFixTimeMs = dt; - } - - return changed ? 1 : 0; -} -//----------------------------------------------------------------------------- diff --git a/Code/Framework/Crcfix/crcfix_files.cmake b/Code/Framework/Crcfix/crcfix_files.cmake deleted file mode 100644 index d170013e67..0000000000 --- a/Code/Framework/Crcfix/crcfix_files.cmake +++ /dev/null @@ -1,11 +0,0 @@ -# -# 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 -# -# - -set(FILES crcfix_files.cmake - crcfix.cpp -) diff --git a/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.cpp b/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.cpp index 9cfc6461c2..8cd28fcbb4 100644 --- a/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.cpp +++ b/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.cpp @@ -10,6 +10,7 @@ #include #include +#include namespace O3DE::ProjectManager { @@ -27,4 +28,14 @@ namespace O3DE::ProjectManager : FormBrowseEditWidget(labelText, "", parent) { } + + void FormBrowseEditWidget::keyPressEvent(QKeyEvent* event) + { + int key = event->key(); + if (key == Qt::Key_Return || key == Qt::Key_Enter) + { + HandleBrowseButton(); + } + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.h b/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.h index 7ec2865240..a1f6948ce9 100644 --- a/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.h +++ b/Code/Tools/ProjectManager/Source/FormBrowseEditWidget.h @@ -24,6 +24,9 @@ namespace O3DE::ProjectManager explicit FormBrowseEditWidget(const QString& labelText = "", QWidget* parent = nullptr); ~FormBrowseEditWidget() = default; + protected: + void keyPressEvent(QKeyEvent* event) override; + protected slots: virtual void HandleBrowseButton() = 0; }; diff --git a/Code/Tools/ProjectManager/Source/FormFolderBrowseEditWidget.cpp b/Code/Tools/ProjectManager/Source/FormFolderBrowseEditWidget.cpp index 86f46400b3..4101f02c9b 100644 --- a/Code/Tools/ProjectManager/Source/FormFolderBrowseEditWidget.cpp +++ b/Code/Tools/ProjectManager/Source/FormFolderBrowseEditWidget.cpp @@ -34,7 +34,6 @@ namespace O3DE::ProjectManager { setText(directory); } - } void FormFolderBrowseEditWidget::setText(const QString& text) @@ -42,4 +41,5 @@ namespace O3DE::ProjectManager QString path = QDir::toNativeSeparators(text); FormBrowseEditWidget::setText(path); } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp index e688ebccd4..de727498fc 100644 --- a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp @@ -9,6 +9,15 @@ #include "DebugOutput.h" #include +#include +#include +#include +#include +#include +#include +#include +#include + namespace AZ::SceneAPI::Utilities { void DebugOutput::Write(const char* name, const char* data) @@ -118,4 +127,63 @@ namespace AZ::SceneAPI::Utilities { return m_output; } + + void WriteAndLog(AZ::IO::SystemFile& dbgFile, const char* strToWrite) + { + AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "%s", strToWrite); + dbgFile.Write(strToWrite, strlen(strToWrite)); + dbgFile.Write("\n", strlen("\n")); + } + + void DebugOutput::BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr& scene, AZStd::string productName) + { + const int debugSceneGraphVersion = 1; + AZStd::string debugSceneFile; + + AzFramework::StringFunc::Path::ConstructFull(outputFolder, productName.c_str(), debugSceneFile); + AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "outputFolder %s, name %s.\n", outputFolder, productName.c_str()); + + AZ::IO::SystemFile dbgFile; + if (dbgFile.Open(debugSceneFile.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY)) + { + WriteAndLog(dbgFile, AZStd::string::format("ProductName: %s", productName.c_str()).c_str()); + WriteAndLog(dbgFile, AZStd::string::format("debugSceneGraphVersion: %d", debugSceneGraphVersion).c_str()); + WriteAndLog(dbgFile, scene->GetName().c_str()); + + const AZ::SceneAPI::Containers::SceneGraph& sceneGraph = scene->GetGraph(); + auto names = sceneGraph.GetNameStorage(); + auto content = sceneGraph.GetContentStorage(); + auto pairView = AZ::SceneAPI::Containers::Views::MakePairView(names, content); + auto view = AZ::SceneAPI::Containers::Views::MakeSceneGraphDownwardsView< + AZ::SceneAPI::Containers::Views::BreadthFirst>( + sceneGraph, sceneGraph.GetRoot(), pairView.cbegin(), true); + + for (auto&& viewIt : view) + { + if (viewIt.second == nullptr) + { + continue; + } + + AZ::SceneAPI::DataTypes::IGraphObject* graphObject = const_cast(viewIt.second.get()); + + WriteAndLog(dbgFile, AZStd::string::format("Node Name: %s", viewIt.first.GetName()).c_str()); + WriteAndLog(dbgFile, AZStd::string::format("Node Path: %s", viewIt.first.GetPath()).c_str()); + WriteAndLog(dbgFile, AZStd::string::format("Node Type: %s", graphObject->RTTI_GetTypeName()).c_str()); + + AZ::SceneAPI::Utilities::DebugOutput debugOutput; + viewIt.second->GetDebugOutput(debugOutput); + + if (!debugOutput.GetOutput().empty()) + { + WriteAndLog(dbgFile, debugOutput.GetOutput().c_str()); + } + } + dbgFile.Close(); + + static const AZ::Data::AssetType dbgSceneGraphAssetType("{07F289D1-4DC7-4C40-94B4-0A53BBCB9F0B}"); + productList.AddProduct(productName, AZ::Uuid::CreateName(productName.c_str()), dbgSceneGraphAssetType, + AZStd::nullopt, AZStd::nullopt); + } + } } diff --git a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h index 5fe5b98243..e05d10e2fa 100644 --- a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h +++ b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h @@ -16,6 +16,22 @@ #include #include +namespace AZ +{ + namespace SceneAPI + { + namespace Containers + { + class Scene; + } + namespace Events + { + struct ExportProduct; + class ExportProductList; + } + } +} + namespace AZ::SceneAPI::Utilities { class DebugOutput @@ -42,6 +58,8 @@ namespace AZ::SceneAPI::Utilities SCENE_CORE_API const AZStd::string& GetOutput() const; + SCENE_CORE_API static void BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr& scene, AZStd::string productName); + protected: AZStd::string m_output; }; diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/Aces.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/Aces.azsli index f81a8cdf37..b7a6c63dc2 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/Aces.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/Aces.azsli @@ -75,6 +75,8 @@ UNDISCLOSED. //////////////////////////////////////////////////////////////////////////////// // Constants +#pragma once + #include static const float HALF_MAX = 65504.0f; @@ -90,7 +92,8 @@ static const float DIM_SURROUND_GAMMA = 0.9811; enum class ShaperType { ShaperLinear, - ShaperLog2 + ShaperLog2, + PqSmpteSt2084, }; //////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/Shapers.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/Shapers.azsli new file mode 100644 index 0000000000..a101867736 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PostProcessing/Shapers.azsli @@ -0,0 +1,52 @@ +/* + * 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 + +// Perceptual quantizer coefficients +static const float PqM1 = 1305.0 / 8192.0; +static const float PqM2 = 2533.0 / 32.0; +static const float PqC1 = 102.0 / 128.0; +static const float PqC2 = 2413.0 / 128.0; +static const float PqC3 = 2392.0 / 128.0; +static const float PqMaxNits = 10000.0; + +float3 ShaperToLinear(float3 shaperColor, ShaperType shaperType, float shaperBias, float shaperScale) +{ + // Apply the inverse of the shaper function to give the color in the working color space + switch (shaperType) + { + case ShaperType::ShaperLinear: + return (shaperColor - shaperBias) / shaperScale; + case ShaperType::ShaperLog2: + return pow(2.0, (shaperColor - shaperBias) / shaperScale); + case ShaperType::PqSmpteSt2084: + shaperColor = min(shaperColor, 1.0); + return PqMaxNits * pow(max(pow(shaperColor, 1.0 / PqM2) - PqC1, 0.0) / (PqC2 - PqC3 * pow(shaperColor, 1.0 / PqM2)), 1.0 / PqM1); + } + return shaperColor; +} + +float3 LinearToShaper(float3 linearColor, ShaperType shaperType, float shaperBias, float shaperScale) +{ + // Convert from working color space to lut coordinates by applying the shaper function + switch (shaperType) + { + case ShaperType::ShaperLinear: + return linearColor * shaperScale + shaperBias; + case ShaperType::ShaperLog2: + return log2(linearColor) * shaperScale + shaperBias; + case ShaperType::PqSmpteSt2084: + linearColor = min(linearColor, PqMaxNits); + linearColor = linearColor / PqMaxNits; + return pow((PqC1 + PqC2 * pow(linearColor, PqM1)) / (1.0 + PqC3 * pow(linearColor, PqM1)), PqM2); + } + return linearColor; +} diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ApplyShaperLookupTable.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ApplyShaperLookupTable.azsl index a180b09630..10d2eb1179 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ApplyShaperLookupTable.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/ApplyShaperLookupTable.azsl @@ -11,9 +11,7 @@ #include #include #include - -static const int SHAPER_LINEAR = 0; -static const int SHAPER_LOG2 = 1; +#include ShaderResourceGroup PassSrg : SRG_PerPass { @@ -41,36 +39,22 @@ PSOutput MainPS(VSOutput IN) float2 uvCoord = float2(IN.m_texCoord.x, IN.m_texCoord.y); float3 color = PassSrg::m_colorTexture.Sample(PassSrg::LinearSampler, uvCoord).rgb; + ShaperType shaperType = (ShaperType)PassSrg::m_shaperType; + // Convert from working color space to lut coordinates by applying the shaper function - float3 lutCoordinate = color; - if (PassSrg::m_shaperType == SHAPER_LINEAR) - { - lutCoordinate = color * PassSrg::m_shaperScale + PassSrg::m_shaperBias; - } - else if (PassSrg::m_shaperType == SHAPER_LOG2) - { - lutCoordinate = log2(color) * PassSrg::m_shaperScale + PassSrg::m_shaperBias; - } + float3 lutCoordinate = LinearToShaper(color, shaperType, PassSrg::m_shaperBias, PassSrg::m_shaperScale); // Adjust coordinate to the domain excluding the outer half texel in all directions uint3 outputDimensions; PassSrg::m_lut.GetDimensions(outputDimensions.x, outputDimensions.y, outputDimensions.z); - float3 coordBias = 1.0/(2.0 * outputDimensions); - float3 coordScale = (outputDimensions-1.0)/outputDimensions; + float3 coordBias = 1.0 / (2.0 * outputDimensions); + float3 coordScale = (outputDimensions - 1.0) / outputDimensions; lutCoordinate = (lutCoordinate * coordScale) + coordBias; float3 lutColor = PassSrg::m_lut.Sample(PassSrg::LinearSampler, lutCoordinate).rgb; // Apply the inverse of the shaper function to give the color in the working color space - float3 finalColor = lutColor; - if (PassSrg::m_shaperType == SHAPER_LINEAR) - { - finalColor = (lutColor - PassSrg::m_shaperBias)/PassSrg::m_shaperScale; - } - else if (PassSrg::m_shaperType == SHAPER_LOG2) - { - finalColor = pow(2.0, (lutColor - PassSrg::m_shaperBias)/PassSrg::m_shaperScale); - } + float3 finalColor = ShaperToLinear(lutColor, shaperType, PassSrg::m_shaperBias, PassSrg::m_shaperScale); OUT.m_color.rgb = finalColor; OUT.m_color.a = 1.0; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BlendColorGradingLuts.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BlendColorGradingLuts.azsl index 6f97996c29..746e18cfc2 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BlendColorGradingLuts.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/BlendColorGradingLuts.azsl @@ -8,6 +8,7 @@ #include #include +#include ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback { @@ -62,40 +63,18 @@ ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback [[range(0, 4)]] option uint o_numSourceLuts = 0; -float3 ShaperToLinear(float3 shaperColor, ShaperType shaperType, float shaperBias, float shaperScale) -{ - // Apply the inverse of the shaper function to give the color in the working color space - float3 linearColor = shaperColor; - if (shaperType == ShaperType::ShaperLinear) - { - linearColor = (shaperColor - shaperBias)/shaperScale; - } - else if (shaperType == ShaperType::ShaperLog2) - { - linearColor = pow(2.0, (shaperColor - shaperBias)/shaperScale); - } - return linearColor; -} - -float3 LinearToShaper(float3 linearColor, ShaperType shaperType, float shaperBias, float shaperScale) -{ - // Convert from working color space to lut coordinates by applying the shaper function - float3 shaperColor = linearColor; - if (shaperType == ShaperType::ShaperLinear) - { - shaperColor = linearColor * shaperScale + shaperBias; - } - else if (shaperType == ShaperType::ShaperLog2) - { - shaperColor = log2(linearColor) * shaperScale + shaperBias; - } - return shaperColor; -} - float3 GetSourceLutLinearColor(float3 baseColor, Texture3D sourceLut, ShaperType shaperType, float shaperBias, float shaperScale) { // Convert from reference linearColor to the lutCoordinate for this Lut float3 lutCoord = LinearToShaper(baseColor, shaperType, shaperBias, shaperScale); + + // Adjust coordinate to the domain excluding the outer half texel in all directions + uint3 outputDimensions; + sourceLut.GetDimensions(outputDimensions.x, outputDimensions.y, outputDimensions.z); + float3 coordBias = 1.0 / (2.0 * outputDimensions); + float3 coordScale = (outputDimensions - 1.0) / outputDimensions; + lutCoord = (lutCoord * coordScale) + coordBias; + float3 lutColor = sourceLut.SampleLevel(PassSrg::LinearSampler, lutCoord, 0).rgb; // Convert to linear float3 linearColor = ShaperToLinear(lutColor, shaperType, shaperBias, shaperScale); @@ -115,11 +94,7 @@ void MainCS(uint3 dispatch_id: SV_DispatchThreadID) } // Get coordinates within the blended LUT 3D texture - float3 baseCoord = float3 ( - (float)(dispatch_id.x)/(float)PassSrg::m_blendedLutDimensions.x, - (float)(dispatch_id.y)/(float)PassSrg::m_blendedLutDimensions.y, - (float)(dispatch_id.z)/(float)PassSrg::m_blendedLutDimensions.z - ); + float3 baseCoord = float3(outPixel) / float3(PassSrg::m_blendedLutDimensions - 1.0); // Convert to the base linear color (this is the color of the identity LUT) float3 baseColor = ShaperToLinear(baseCoord, (ShaperType)PassSrg::m_blendedLutShaperType, PassSrg::m_blendedLutShaperBias, PassSrg::m_blendedLutShaperScale); diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LookModificationTransform.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LookModificationTransform.azsl index 8b1841c5e1..4741b01617 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LookModificationTransform.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LookModificationTransform.azsl @@ -12,6 +12,7 @@ #include #include #include +#include #include "EyeAdaptationUtil.azsli" ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback @@ -42,12 +43,111 @@ option bool o_enableExposureControlFeature = false; // Option shader variable to enable color grading LUT. option bool o_enableColorGradingLut = false; +// Controls the sampling quality of the blended LUT. Setting this higher can improve the quality of particularly tricky luts. +// 0 - linear +// 1 - 7 tap b-spline +// 2 - 19 tap b-spline +[[range(0, 2)]] +option uint o_lutSampleQuality = 0; + +// Sample a 3dtexture with a 7 or 19 tap B-Spline. Consider ripping this out and putting in a more general location. +// This function samples a 4x4x4 neighborhood around the uv. Normally this would take 64 samples, but by taking +// advantage of bilinear filtering this can be done with 27 taps on the edges between pixels. The cost is further +// reduced by dropping either the 8 corners (19 total taps) or also dropping the 12 edges (7 total taps). +float4 SampleBSpline3D(Texture3D texture, SamplerState linearSampler, float3 uv, float3 textureSize, float3 rcpTextureSize) +{ + // Think of sample locations in the 4x4 neighborhood as having a top left coordinate of 0,0 and + // a bottom right coordinate of 3,3. + + // Find the position in texture space then round it to get the center of the 1,1 pixel (tc1) + float3 texelPos = uv * textureSize; + float3 tc1= floor(texelPos - 0.5) + 0.5; + + // Offset from center position to texel + float3 f = texelPos - tc1; + + // Compute B-Spline weights based on the offset + float3 OneMinusF = (1.0 - f); + float3 OneMinusF2 = OneMinusF * OneMinusF; + float3 OneMinusF3 = OneMinusF2 * OneMinusF; + float3 w0 = OneMinusF3; + float3 w1 = 4.0 + 3.0 * f * f * f - 6.0 * f * f; + float3 w2 = 4.0 + 3.0 * OneMinusF3 - 6.0 * OneMinusF2; + float3 w3 = f * f * f; + + float3 w12 = w1 + w2; + + // Compute uv coordinates for sampling the texture + float3 tc0 = (tc1 - 1.0f) * rcpTextureSize; + float3 tc3 = (tc1 + 2.0f) * rcpTextureSize; + float3 tc12 = (tc1 + w2 / w12) * rcpTextureSize; + + // Compute sample weights + float sw0 = w12.x * w0.y * w12.z; + float sw1 = w0.x * w12.y * w12.z; + float sw2 = w12.x * w12.y * w12.z; + float sw3 = w3.x * w12.y * w12.z; + float sw4 = w12.x * w3.y * w12.z; + float sw5 = w12.x * w12.y * w0.z; + float sw6 = w12.x * w12.y * w3.z; + + // total weight of samples to normalize result. + float totalWeight = sw0 + sw1 + sw2 + sw3 + sw4 + sw5 + sw6; + + float4 result = 0.0f; + result += texture.SampleLevel(linearSampler, float3(tc12.x, tc0.y, tc12.z), 0.0) * sw0; + result += texture.SampleLevel(linearSampler, float3( tc0.x, tc12.y, tc12.z), 0.0) * sw1; + result += texture.SampleLevel(linearSampler, float3(tc12.x, tc12.y, tc12.z), 0.0) * sw2; + result += texture.SampleLevel(linearSampler, float3( tc3.x, tc12.y, tc12.z), 0.0) * sw3; + result += texture.SampleLevel(linearSampler, float3(tc12.x, tc3.y, tc12.z), 0.0) * sw4; + result += texture.SampleLevel(linearSampler, float3(tc12.x, tc12.y, tc0.z), 0.0) * sw5; + result += texture.SampleLevel(linearSampler, float3(tc12.x, tc12.y, tc3.z), 0.0) * sw6; + + if (o_lutSampleQuality == 2) + { + // Extra 12 taps for Diagonals to increase the quality further. + + float sw7 = w0.x * w0.y * w12.z; + float sw8 = w0.x * w3.y * w12.z; + float sw9 = w3.x * w0.y * w12.z; + float sw10 = w3.x * w3.y * w12.z; + + float sw11 = w12.x * w0.y * w0.z; + float sw12 = w12.x * w0.y * w3.z; + float sw13 = w12.x * w3.y * w0.z; + float sw14 = w12.x * w3.y * w3.z; + + float sw15 = w0.x * w12.y * w0.z; + float sw16 = w0.x * w12.y * w3.z; + float sw17 = w3.x * w12.y * w0.z; + float sw18 = w3.x * w12.y * w3.z; + + totalWeight += sw7 + sw8 + sw9 + sw10 + sw11 + sw12 + sw13 + sw14 + sw15 + sw16 + sw17 + sw18; + + result += texture.SampleLevel(linearSampler, float3(tc0.x, tc0.y, tc12.z), 0.0) * sw7; + result += texture.SampleLevel(linearSampler, float3(tc0.x, tc3.y, tc12.z), 0.0) * sw8; + result += texture.SampleLevel(linearSampler, float3(tc3.x, tc0.y, tc12.z), 0.0) * sw9; + result += texture.SampleLevel(linearSampler, float3(tc3.x, tc3.y, tc12.z), 0.0) * sw10; + + result += texture.SampleLevel(linearSampler, float3(tc12.x, tc0.y, tc0.z), 0.0) * sw11; + result += texture.SampleLevel(linearSampler, float3(tc12.x, tc0.y, tc3.z), 0.0) * sw12; + result += texture.SampleLevel(linearSampler, float3(tc12.x, tc3.y, tc0.z), 0.0) * sw13; + result += texture.SampleLevel(linearSampler, float3(tc12.x, tc3.y, tc3.z), 0.0) * sw14; + + result += texture.SampleLevel(linearSampler, float3(tc0.x, tc12.y, tc0.z), 0.0) * sw15; + result += texture.SampleLevel(linearSampler, float3(tc0.x, tc12.y, tc3.z), 0.0) * sw16; + result += texture.SampleLevel(linearSampler, float3(tc3.x, tc12.y, tc0.z), 0.0) * sw17; + result += texture.SampleLevel(linearSampler, float3(tc3.x, tc12.y, tc3.z), 0.0) * sw18; + } + return result / totalWeight; +} + PSOutput MainPS(VSOutput IN) { PSOutput OUT; // Fetch the pixel color from the input texture - float3 color = PassSrg::m_framebuffer.Sample(PassSrg::LinearSampler, IN.m_texCoord).rgb; + float3 color = PassSrg::m_framebuffer.SampleLevel(PassSrg::LinearSampler, IN.m_texCoord, 0.0).rgb; if (o_enableExposureControlFeature) { @@ -63,36 +163,28 @@ PSOutput MainPS(VSOutput IN) if (o_enableColorGradingLut) { // Convert from working color space to lut coordinates by applying the shaper function - float3 lutCoordinate = color; - if (shaperType == ShaperType::ShaperLinear) - { - lutCoordinate = color * PassSrg::m_shaperScale + PassSrg::m_shaperBias; - } - else if (shaperType == ShaperType::ShaperLog2) - { - lutCoordinate = log2(color) * PassSrg::m_shaperScale + PassSrg::m_shaperBias; - } + float3 lutCoordinate = LinearToShaper(color, shaperType, PassSrg::m_shaperBias, PassSrg::m_shaperScale); // Adjust coordinate to the domain excluding the outer half texel in all directions uint3 outputDimensions; PassSrg::m_gradingLut.GetDimensions(outputDimensions.x, outputDimensions.y, outputDimensions.z); float3 coordBias = 0.5f / outputDimensions; - float3 coordScale = (outputDimensions-1.0)/outputDimensions; + float3 sizeMinusOne = outputDimensions - 1.0; + float3 coordScale = sizeMinusOne / outputDimensions; lutCoordinate = (lutCoordinate * coordScale) + coordBias; - float3 lutColor = PassSrg::m_gradingLut.Sample(PassSrg::LinearSampler, lutCoordinate).rgb; + float3 lutColor = float3(0.0, 0.0, 0.0); + if (o_lutSampleQuality == 0) + { + lutColor = PassSrg::m_gradingLut.SampleLevel(PassSrg::LinearSampler, lutCoordinate, 0.0).rgb; + } + else + { + lutColor = SampleBSpline3D(PassSrg::m_gradingLut, PassSrg::LinearSampler, lutCoordinate, float3(outputDimensions), 1.0 / float3(outputDimensions)).rgb; + } // Apply the inverse of the shaper function to give the color in the working color space - float3 finalColor = lutColor; - if (shaperType == ShaperType::ShaperLinear) - { - finalColor = (lutColor - PassSrg::m_shaperBias)/PassSrg::m_shaperScale; - } - else if (shaperType == ShaperType::ShaperLog2) - { - finalColor = pow(2.0, (lutColor - PassSrg::m_shaperBias)/PassSrg::m_shaperScale); - } - color = finalColor; + color = ShaperToLinear(lutColor, shaperType, PassSrg::m_shaperBias, PassSrg::m_shaperScale); } OUT.m_color.rgb = color; diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LookModificationTransform.shadervariantlist b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LookModificationTransform.shadervariantlist index 0de004bde6..88d6bf9b4e 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LookModificationTransform.shadervariantlist +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/LookModificationTransform.shadervariantlist @@ -1,9 +1,13 @@ { "Shader" : "LookModificationTransform.shader", "Variants" : [ - { "StableId": 1, "Options" : { "o_enableExposureControlFeature": "true", "o_enableColorGradingLut": "true" } }, - { "StableId": 2, "Options" : { "o_enableExposureControlFeature": "true", "o_enableColorGradingLut": "false" } }, - { "StableId": 3, "Options" : { "o_enableExposureControlFeature": "false", "o_enableColorGradingLut": "true" } }, - { "StableId": 4, "Options" : { "o_enableExposureControlFeature": "false", "o_enableColorGradingLut": "false" } } + { "StableId": 1, "Options" : { "o_enableExposureControlFeature": "true", "o_enableColorGradingLut": "false" } }, + { "StableId": 2, "Options" : { "o_enableExposureControlFeature": "false", "o_enableColorGradingLut": "false" } }, + { "StableId": 3, "Options" : { "o_enableExposureControlFeature": "true", "o_enableColorGradingLut": "true", "o_lutSampleQuality": 0 } }, + { "StableId": 4, "Options" : { "o_enableExposureControlFeature": "false", "o_enableColorGradingLut": "true", "o_lutSampleQuality": 0 } }, + { "StableId": 5, "Options" : { "o_enableExposureControlFeature": "true", "o_enableColorGradingLut": "true", "o_lutSampleQuality": 1 } }, + { "StableId": 6, "Options" : { "o_enableExposureControlFeature": "false", "o_enableColorGradingLut": "true", "o_lutSampleQuality": 1 } }, + { "StableId": 7, "Options" : { "o_enableExposureControlFeature": "true", "o_enableColorGradingLut": "true", "o_lutSampleQuality": 2 } }, + { "StableId": 8, "Options" : { "o_enableExposureControlFeature": "false", "o_enableColorGradingLut": "true", "o_lutSampleQuality": 2 } } ] } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAA.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAA.azsli index 58c2bc1ce9..8c03816b26 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAA.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/SMAA.azsli @@ -1135,13 +1135,13 @@ float4 SMAABlendingWeightCalculationPS(float2 texcoord, if (!o_enableDiagonalDetectionFeature || weights.r == -weights.g) // weights.r + weights.g == 0.0 { - float2 d; + // NOTE: using separate floats for (dx, dy) and (sqrt_d_x, sqrt_d_y) instead of float2 due to android Mali driver problem crashing the device // Find the distance to the left: float3 coords; coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) - d.x = coords.x; + float dx = coords.x; // Now fetch the left crossing edges, two at a time using bilinear // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to @@ -1150,26 +1150,29 @@ float4 SMAABlendingWeightCalculationPS(float2 texcoord, // Find the distance to the right: coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); - d.y = coords.z; + float dy = coords.z; // We want the distances to be in pixel units (doing this here allow to // better interleave arithmetic and memory accesses): - d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); + dx = abs(round(mad(SMAA_RT_METRICS.z, dx, -pixcoord.x))); + dy = abs(round(mad(SMAA_RT_METRICS.z, dy, -pixcoord.x))); // SMAAArea below needs a sqrt, as the areas texture is compressed // quadratically: - float2 sqrt_d = sqrt(d); + float sqrt_d_x = sqrt(dx); + float sqrt_d_y = sqrt(dy); + // Fetch the right crossing edges: float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; // Ok, we know how this pattern looks like, now it is time for getting // the actual area: - weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); + weights.rg = SMAAArea(SMAATexturePass2D(areaTex), float2(sqrt_d_x, sqrt_d_y), e1, e2, subsampleIndices.y); // Fix corners: coords.y = texcoord.y; - SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); + SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, float2(dx, dy)); } else { @@ -1180,37 +1183,37 @@ float4 SMAABlendingWeightCalculationPS(float2 texcoord, SMAA_BRANCH if (e.r > 0.0) // Edge at west { - float2 d; - // Find the distance to the top: float3 coords; coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; - d.x = coords.y; + float dx = coords.y; // Fetch the top crossing edges: float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; // Find the distance to the bottom: coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); - d.y = coords.z; + float dy = coords.z; // We want the distances to be in pixel units: - d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); + dx = abs(round(mad(SMAA_RT_METRICS.w, dx, -pixcoord.y))); + dy = abs(round(mad(SMAA_RT_METRICS.w, dy, -pixcoord.y))); // SMAAArea below needs a sqrt, as the areas texture is compressed // quadratically: - float2 sqrt_d = sqrt(d); + float sqrt_d_x = sqrt(dx); + float sqrt_d_y = sqrt(dy); // Fetch the bottom crossing edges: float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, 1)).g; // Get the area for this direction: - weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); + weights.ba = SMAAArea(SMAATexturePass2D(areaTex), float2(sqrt_d_x, sqrt_d_y), e1, e2, subsampleIndices.x); // Fix corners: coords.x = texcoord.x; - SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); + SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, float2(dx, dy)); } return weights; diff --git a/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.cpp b/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.cpp index 125614235f..79f16ee628 100644 --- a/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.cpp +++ b/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.cpp @@ -203,47 +203,37 @@ namespace AZ return ODT_48nits; } + ShaperParams GetLog2ShaperParameters(float minStops, float maxStops) + { + ShaperParams shaperParams; + + constexpr float Log2MediumGray = -2.47393118833f; // log2f(0.18f); + shaperParams.m_type = ShaperType::Log2; + shaperParams.m_scale = 1.0f / (maxStops - minStops); + shaperParams.m_bias = -((minStops + Log2MediumGray) * shaperParams.m_scale); + + return shaperParams; + } + ShaperParams GetAcesShaperParameters(OutputDeviceTransformType odtType) { AZ_Assert(static_cast(odtType) < static_cast(NumOutputDeviceTransformTypes), "Invalid ODT type specified."); - ShaperParams shaperParams; - - // These values represent and low and high end of the dynamic range in terms of stops from middle grey (0.18) - float lowerDynamicRangeInStops; - float higherDynamicRangeInStops; - const float MIDDLE_GREY = 0.18f; - switch (odtType) { case OutputDeviceTransformType_48Nits: - lowerDynamicRangeInStops = -6.5f; - higherDynamicRangeInStops = 6.5f; - break; + return GetLog2ShaperParameters(-6.5f, 6.5f); case OutputDeviceTransformType_1000Nits: - lowerDynamicRangeInStops = -12.f; - higherDynamicRangeInStops = 10.f; - break; + return GetLog2ShaperParameters(-12.0f, 10.0f); case OutputDeviceTransformType_2000Nits: - lowerDynamicRangeInStops = -12.f; - higherDynamicRangeInStops = 11.f; - break; + return GetLog2ShaperParameters(-12.0f, 11.0f); case OutputDeviceTransformType_4000Nits: - lowerDynamicRangeInStops = -12.f; - higherDynamicRangeInStops = 12.f; - break; + return GetLog2ShaperParameters(-12.0f, 12.0f); default: AZ_Assert(false, "Invalid output device transform type."); - return shaperParams; break; } - - float logMin = log2(MIDDLE_GREY * exp2(lowerDynamicRangeInStops)); - float logMax = log2(MIDDLE_GREY * exp2(higherDynamicRangeInStops)); - shaperParams.scale = 1.0f / (logMax - logMin); - shaperParams.bias = -shaperParams.scale * logMin; - shaperParams.type = ShaperType::Log2; - return shaperParams; + return ShaperParams(); } Matrix3x3 GetColorConvertionMatrix(ColorConvertionMatrixType type) diff --git a/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.h b/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.h index 94d6434b93..3ec2885a99 100644 --- a/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.h +++ b/Gems/Atom/Feature/Common/Code/3rdParty/ACES/ACES/Aces.h @@ -124,18 +124,19 @@ namespace AZ NumColorConvertionMatrixTypes }; - enum ShaperType + enum class ShaperType : uint32_t { Linear = 0, Log2 = 1, + PqSmpteSt2084 = 2, NumShaperTypes }; struct ShaperParams { - ShaperType type = ShaperType::Linear; - float bias = 0.f; - float scale = 1.f; + ShaperType m_type = ShaperType::Linear; + float m_bias = 0.0f; + float m_scale = 1.0f; }; enum class DisplayMapperOperationType : uint32_t @@ -151,10 +152,14 @@ namespace AZ enum class ShaperPresetType { None = 0, - Log2_48_nits, - Log2_1000_nits, - Log2_2000_nits, - Log2_4000_nits + LinearCustomRange, + Log2_48Nits, + Log2_1000Nits, + Log2_2000Nits, + Log2_4000Nits, + Log2CustomRange, + PqSmpteSt2084, + NumShaperTypes }; enum class ToneMapperType @@ -171,6 +176,7 @@ namespace AZ }; SegmentedSplineParamsC9 GetAcesODTParameters(OutputDeviceTransformType odtType); + ShaperParams GetLog2ShaperParameters(float minStops, float maxStops); ShaperParams GetAcesShaperParameters(OutputDeviceTransformType odtType); Matrix3x3 GetColorConvertionMatrix(ColorConvertionMatrixType type); diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h index 7d2aa18f29..6be81c8cae 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h @@ -78,7 +78,7 @@ namespace AZ static OutputDeviceTransformType GetOutputDeviceTransformType(RHI::Format bufferFormat); static void GetAcesDisplayMapperParameters(DisplayMapperParameters* displayMapperParameters, OutputDeviceTransformType odtType); - static ShaperParams GetShaperParameters(ShaperPresetType shaperPreset); + static ShaperParams GetShaperParameters(ShaperPresetType shaperPreset, float customMinEv = 0.0f, float customMaxEv = 0.0f); static void GetDefaultDisplayMapperConfiguration(DisplayMapperConfigurationDescriptor& config); // DisplayMapperFeatureProcessorInteface overrides... @@ -102,8 +102,6 @@ namespace AZ static constexpr const char* FeatureProcessorName = "AcesDisplayMapperFeatureProcessor"; - static const int LutSize = 32; - static const RHI::Format LutFormat = RHI::Format::R16G16B16A16_FLOAT; static const int ImagePoolBudget = 1 << 20; // 1 Megabyte // LUTs that are baked through shaders diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/LookModification/LookModificationParams.inl b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/LookModification/LookModificationParams.inl index 38a1987caa..4c3cf8ac92 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/LookModification/LookModificationParams.inl +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/LookModification/LookModificationParams.inl @@ -10,11 +10,9 @@ // PARAM(NAME, MEMBER_NAME, DEFAULT_VALUE, ...) AZ_GFX_BOOL_PARAM(Enabled, m_enabled, false) - AZ_GFX_COMMON_PARAM(Data::Asset, ColorGradingLut, m_colorGradingLut, {}) - -AZ_GFX_COMMON_PARAM(AZ::Render::ShaperPresetType, ShaperPresetType, m_shaperPresetType, AZ::Render::ShaperPresetType::Log2_48_nits) - +AZ_GFX_COMMON_PARAM(AZ::Render::ShaperPresetType, ShaperPresetType, m_shaperPresetType, AZ::Render::ShaperPresetType::Log2_48Nits) +AZ_GFX_COMMON_PARAM(float, CustomMinExposure, m_customMinExposure, -6.5) +AZ_GFX_COMMON_PARAM(float, CustomMaxExposure, m_customMaxExposure, 6.5) AZ_GFX_FLOAT_PARAM(ColorGradingLutIntensity, m_colorGradingLutIntensity, 1.0) - AZ_GFX_FLOAT_PARAM(ColorGradingLutOverride, m_colorGradingLutOverride, 1.0) diff --git a/Gems/Atom/Feature/Common/Code/Source/ACES/AcesDisplayMapperFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ACES/AcesDisplayMapperFeatureProcessor.cpp index 4c057aab2a..c6c41edb31 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ACES/AcesDisplayMapperFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ACES/AcesDisplayMapperFeatureProcessor.cpp @@ -21,6 +21,7 @@ namespace { + static const AZ::RHI::Format LutFormat = AZ::RHI::Format::R16G16B16A16_FLOAT; uint16_t ConvertFloatToHalf(const float Value) { @@ -56,395 +57,409 @@ namespace } } -namespace AZ +namespace AZ::Render { - namespace Render + void AcesDisplayMapperFeatureProcessor::Reflect(ReflectContext* context) { - void AcesDisplayMapperFeatureProcessor::Reflect(ReflectContext* context) + if (auto* serializeContext = azrtti_cast(context)) { - if (auto* serializeContext = azrtti_cast(context)) - { - serializeContext - ->Class() - ->Version(0); - } + serializeContext + ->Class() + ->Version(0); + } + } + + void AcesDisplayMapperFeatureProcessor::Activate() + { + GetDefaultDisplayMapperConfiguration(m_displayMapperConfiguration); + } + + void AcesDisplayMapperFeatureProcessor::Deactivate() + { + m_ownedLuts.clear(); + } + + void AcesDisplayMapperFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) + { + AZ_TRACE_METHOD(); + AZ_UNUSED(packet); + } + + void AcesDisplayMapperFeatureProcessor::Render([[maybe_unused]] const FeatureProcessor::RenderPacket& packet) + { + } + + void AcesDisplayMapperFeatureProcessor::ApplyLdrOdtParameters(DisplayMapperParameters* displayMapperParameters) + { + AZ_Assert(displayMapperParameters != nullptr, "The pOutParameters must not to be null pointer."); + if (displayMapperParameters == nullptr) + { + return; } - void AcesDisplayMapperFeatureProcessor::Activate() + // These values in the ODT parameter are taken from the reference ACES transform. + // + // The original ACES references. + // Common: + // https://github.com/ampas/aces-dev/blob/master/transforms/ctl/lib/ACESlib.ODT_Common.ctl + // For sRGB: + // https://github.com/ampas/aces-dev/tree/master/transforms/ctl/odt/sRGB + displayMapperParameters->m_cinemaLimits[0] = 0.02f; + displayMapperParameters->m_cinemaLimits[1] = 48.0f; + displayMapperParameters->m_acesSplineParams = GetAcesODTParameters(OutputDeviceTransformType_48Nits); + displayMapperParameters->m_OutputDisplayTransformFlags = AlterSurround | ApplyDesaturation | ApplyCATD60toD65; + displayMapperParameters->m_OutputDisplayTransformMode = Srgb; + ColorConvertionMatrixType colorMatrixType = XYZ_To_Rec709; + switch (displayMapperParameters->m_OutputDisplayTransformMode) { - GetDefaultDisplayMapperConfiguration(m_displayMapperConfiguration); + case Srgb: + colorMatrixType = XYZ_To_Rec709; + break; + case PerceptualQuantizer: + case Ldr: + colorMatrixType = XYZ_To_Bt2020; + break; + default: + break; + } + displayMapperParameters->m_XYZtoDisplayPrimaries = GetColorConvertionMatrix(colorMatrixType); + + displayMapperParameters->m_surroundGamma = 0.9811f; + displayMapperParameters->m_gamma = 2.2f; + } + + void AcesDisplayMapperFeatureProcessor::ApplyHdrOdtParameters(DisplayMapperParameters* displayMapperParameters, const OutputDeviceTransformType& odtType) + { + AZ_Assert(displayMapperParameters != nullptr, "The pOutParameters must not to be null pointer."); + if (displayMapperParameters == nullptr) + { + return; } - void AcesDisplayMapperFeatureProcessor::Deactivate() + // Dynamic range limit values taken from NVIDIA HDR sample. + // These values represent and low and high end of the dynamic range in terms of stops from middle grey (0.18) + float lowerDynamicRangeInStops = -12.f; + float higherDynamicRangeInStops = 10.f; + const float MIDDLE_GREY = 0.18f; + + switch (odtType) { - m_ownedLuts.clear(); + case OutputDeviceTransformType_1000Nits: + higherDynamicRangeInStops = 10.f; + break; + case OutputDeviceTransformType_2000Nits: + higherDynamicRangeInStops = 11.f; + break; + case OutputDeviceTransformType_4000Nits: + higherDynamicRangeInStops = 12.f; + break; + default: + AZ_Assert(false, "Invalid output device transform type."); + break; } - void AcesDisplayMapperFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) + displayMapperParameters->m_cinemaLimits[0] = MIDDLE_GREY * exp2(lowerDynamicRangeInStops); + displayMapperParameters->m_cinemaLimits[1] = MIDDLE_GREY * exp2(higherDynamicRangeInStops); + displayMapperParameters->m_acesSplineParams = GetAcesODTParameters(odtType); + displayMapperParameters->m_OutputDisplayTransformFlags = AlterSurround | ApplyDesaturation | ApplyCATD60toD65; + displayMapperParameters->m_OutputDisplayTransformMode = PerceptualQuantizer; + ColorConvertionMatrixType colorMatrixType = XYZ_To_Bt2020; + displayMapperParameters->m_XYZtoDisplayPrimaries = GetColorConvertionMatrix(colorMatrixType); + + // Surround gamma value is from the dim surround gamma from the ACES reference transforms. + // https://github.com/ampas/aces-dev/blob/master/transforms/ctl/lib/ACESlib.ODT_Common.ctl + displayMapperParameters->m_surroundGamma = 0.9811f; + displayMapperParameters->m_gamma = 1.0f; // gamma not used with perceptual quantizer, but just set to 1.0 anyways + } + + OutputDeviceTransformType AcesDisplayMapperFeatureProcessor::GetOutputDeviceTransformType(RHI::Format bufferFormat) + { + OutputDeviceTransformType outputDeviceTransformType = OutputDeviceTransformType_48Nits; + if (bufferFormat == RHI::Format::R8G8B8A8_UNORM || + bufferFormat == RHI::Format::B8G8R8A8_UNORM) { - AZ_TRACE_METHOD(); - AZ_UNUSED(packet); + outputDeviceTransformType = OutputDeviceTransformType_48Nits; + } + else if (bufferFormat == RHI::Format::R10G10B10A2_UNORM) + { + outputDeviceTransformType = OutputDeviceTransformType_1000Nits; + } + else + { + AZ_Assert(false, "Not yet supported."); + // To work normally on unsupported environment, initialize the display parameters by OutputDeviceTransformType_48Nits. + outputDeviceTransformType = OutputDeviceTransformType_48Nits; + } + return outputDeviceTransformType; + } + + void AcesDisplayMapperFeatureProcessor::GetAcesDisplayMapperParameters(DisplayMapperParameters* displayMapperParameters, OutputDeviceTransformType odtType) + { + switch (odtType) + { + case OutputDeviceTransformType_48Nits: + ApplyLdrOdtParameters(displayMapperParameters); + break; + case OutputDeviceTransformType_1000Nits: + case OutputDeviceTransformType_2000Nits: + case OutputDeviceTransformType_4000Nits: + ApplyHdrOdtParameters(displayMapperParameters, odtType); + break; + default: + AZ_Assert(false, "This ODT type[%d] is not supported.", odtType); + break; + } + } + + void AcesDisplayMapperFeatureProcessor::GetOwnedLut(DisplayMapperLut& displayMapperLut, const AZ::Name& lutName) + { + auto it = m_ownedLuts.find(lutName); + if (it == m_ownedLuts.end()) + { + InitializeLutImage(lutName); + it = m_ownedLuts.find(lutName); + AZ_Assert(it != m_ownedLuts.end(), "AcesDisplayMapperFeatureProcessor unable to create LUT %s", lutName.GetCStr()); + } + displayMapperLut = it->second; + } + + void AcesDisplayMapperFeatureProcessor::GetDisplayMapperLut(DisplayMapperLut& displayMapperLut) + { + const AZ::Name acesLutName("AcesLutImage"); + auto it = m_ownedLuts.find(acesLutName); + if (it == m_ownedLuts.end()) + { + InitializeLutImage(acesLutName); + + it = m_ownedLuts.find(acesLutName); + AZ_Assert(it != m_ownedLuts.end(), "AcesDisplayMapperFeatureProcessor unable to create ACES LUT image"); + } + displayMapperLut = it->second; + } + + void AcesDisplayMapperFeatureProcessor::GetLutFromAssetLocation(DisplayMapperAssetLut& displayMapperAssetLut, const AZStd::string& assetPath) + { + Data::AssetId assetId = RPI::AssetUtils::GetAssetIdForProductPath(assetPath.c_str(), RPI::AssetUtils::TraceLevel::Error); + GetLutFromAssetId(displayMapperAssetLut, assetId); + } + + void AcesDisplayMapperFeatureProcessor::GetLutFromAssetId(DisplayMapperAssetLut& displayMapperAssetLut, const AZ::Data::AssetId assetId) + { + if (!assetId.IsValid()) + { + return; } - void AcesDisplayMapperFeatureProcessor::Render([[maybe_unused]] const FeatureProcessor::RenderPacket& packet) + // Check first if this already exists + auto it = m_assetLuts.find(assetId.ToString()); + if (it != m_assetLuts.end()) { + displayMapperAssetLut = it->second; + return; } - void AcesDisplayMapperFeatureProcessor::ApplyLdrOdtParameters(DisplayMapperParameters* displayMapperParameters) + // Read the lut which is a .3dl file embedded within an azasset file. + Data::Asset asset = RPI::AssetUtils::LoadAssetById(assetId, RPI::AssetUtils::TraceLevel::Error); + const LookupTableAsset* lutAsset = RPI::GetDataFromAnyAsset(asset); + + if (lutAsset == nullptr) { - AZ_Assert(displayMapperParameters != nullptr, "The pOutParameters must not to be null pointer."); - if (displayMapperParameters == nullptr) - { - return; - } - - // These values in the ODT parameter are taken from the reference ACES transform. - // - // The original ACES references. - // Common: - // https://github.com/ampas/aces-dev/blob/master/transforms/ctl/lib/ACESlib.ODT_Common.ctl - // For sRGB: - // https://github.com/ampas/aces-dev/tree/master/transforms/ctl/odt/sRGB - displayMapperParameters->m_cinemaLimits[0] = 0.02f; - displayMapperParameters->m_cinemaLimits[1] = 48.0f; - displayMapperParameters->m_acesSplineParams = GetAcesODTParameters(OutputDeviceTransformType_48Nits); - displayMapperParameters->m_OutputDisplayTransformFlags = AlterSurround | ApplyDesaturation | ApplyCATD60toD65; - displayMapperParameters->m_OutputDisplayTransformMode = Srgb; - ColorConvertionMatrixType colorMatrixType = XYZ_To_Rec709; - switch (displayMapperParameters->m_OutputDisplayTransformMode) - { - case Srgb: - colorMatrixType = XYZ_To_Rec709; - break; - case PerceptualQuantizer: - case Ldr: - colorMatrixType = XYZ_To_Bt2020; - break; - default: - break; - } - displayMapperParameters->m_XYZtoDisplayPrimaries = GetColorConvertionMatrix(colorMatrixType); - - displayMapperParameters->m_surroundGamma = 0.9811f; - displayMapperParameters->m_gamma = 2.2f; + AZ_Error("AcesDisplayMapperFeatureProcessor", false, "Unable to read LUT from asset."); + asset.Release(); + return; } - void AcesDisplayMapperFeatureProcessor::ApplyHdrOdtParameters(DisplayMapperParameters* displayMapperParameters, const OutputDeviceTransformType& odtType) + // The first row of numbers in a 3dl file is a number of vertices that partition the space from [0,..1023] + // This assumes that the vertices are evenly spaced apart. Non-uniform spacing is supported by the format, + // but haven't been encountered yet. + const size_t lutSize = lutAsset->m_intervals.size(); + + if (lutSize == 0) { - AZ_Assert(displayMapperParameters != nullptr, "The pOutParameters must not to be null pointer."); - if (displayMapperParameters == nullptr) - { - return; - } - - // Dynamic range limit values taken from NVIDIA HDR sample. - // These values represent and low and high end of the dynamic range in terms of stops from middle grey (0.18) - float lowerDynamicRangeInStops = -12.f; - float higherDynamicRangeInStops = 10.f; - const float MIDDLE_GREY = 0.18f; - - switch (odtType) - { - case OutputDeviceTransformType_1000Nits: - higherDynamicRangeInStops = 10.f; - break; - case OutputDeviceTransformType_2000Nits: - higherDynamicRangeInStops = 11.f; - break; - case OutputDeviceTransformType_4000Nits: - higherDynamicRangeInStops = 12.f; - break; - default: - AZ_Assert(false, "Invalid output device transform type."); - break; - } - - displayMapperParameters->m_cinemaLimits[0] = MIDDLE_GREY * exp2(lowerDynamicRangeInStops); - displayMapperParameters->m_cinemaLimits[1] = MIDDLE_GREY * exp2(higherDynamicRangeInStops); - displayMapperParameters->m_acesSplineParams = GetAcesODTParameters(odtType); - displayMapperParameters->m_OutputDisplayTransformFlags = AlterSurround | ApplyDesaturation | ApplyCATD60toD65; - displayMapperParameters->m_OutputDisplayTransformMode = PerceptualQuantizer; - ColorConvertionMatrixType colorMatrixType = XYZ_To_Bt2020; - displayMapperParameters->m_XYZtoDisplayPrimaries = GetColorConvertionMatrix(colorMatrixType); - - // Surround gamma value is from the dim surround gamma from the ACES reference transforms. - // https://github.com/ampas/aces-dev/blob/master/transforms/ctl/lib/ACESlib.ODT_Common.ctl - displayMapperParameters->m_surroundGamma = 0.9811f; - displayMapperParameters->m_gamma = 1.0f; // gamma not used with perceptual quantizer, but just set to 1.0 anyways + AZ_Error("AcesDisplayMapperFeatureProcessor", false, "Lut asset has invalid size."); + asset.Release(); + return; } - OutputDeviceTransformType AcesDisplayMapperFeatureProcessor::GetOutputDeviceTransformType(RHI::Format bufferFormat) + // Create a buffer of half floats from the LUT and use it to initialize a 3d texture. + + const size_t kChannels = 4; + const size_t kChannelBytes = 2; + const size_t bytesPerRow = lutSize * kChannels * kChannelBytes; + const size_t bytesPerSlice = bytesPerRow * lutSize; + + AZStd::vector u16Buffer; + const size_t bufferSize = lutSize * lutSize * lutSize * kChannels; + u16Buffer.resize(bufferSize); + + for (size_t slice = 0; slice < lutSize; slice++) { - OutputDeviceTransformType outputDeviceTransformType = OutputDeviceTransformType_48Nits; - if (bufferFormat == RHI::Format::R8G8B8A8_UNORM || - bufferFormat == RHI::Format::B8G8R8A8_UNORM) + for (size_t column = 0; column < lutSize; column++) { - outputDeviceTransformType = OutputDeviceTransformType_48Nits; - } - else if (bufferFormat == RHI::Format::R10G10B10A2_UNORM) - { - outputDeviceTransformType = OutputDeviceTransformType_1000Nits; - } - else - { - AZ_Assert(false, "Not yet supported."); - // To work normally on unsupported environment, initialize the display parameters by OutputDeviceTransformType_48Nits. - outputDeviceTransformType = OutputDeviceTransformType_48Nits; - } - return outputDeviceTransformType; - } - - void AcesDisplayMapperFeatureProcessor::GetAcesDisplayMapperParameters(DisplayMapperParameters* displayMapperParameters, OutputDeviceTransformType odtType) - { - switch (odtType) - { - case OutputDeviceTransformType_48Nits: - ApplyLdrOdtParameters(displayMapperParameters); - break; - case OutputDeviceTransformType_1000Nits: - case OutputDeviceTransformType_2000Nits: - case OutputDeviceTransformType_4000Nits: - ApplyHdrOdtParameters(displayMapperParameters, odtType); - break; - default: - AZ_Assert(false, "This ODT type[%d] is not supported.", odtType); - break; - } - } - - void AcesDisplayMapperFeatureProcessor::GetOwnedLut(DisplayMapperLut& displayMapperLut, const AZ::Name& lutName) - { - auto it = m_ownedLuts.find(lutName); - if (it == m_ownedLuts.end()) - { - InitializeLutImage(lutName); - it = m_ownedLuts.find(lutName); - AZ_Assert(it != m_ownedLuts.end(), "AcesDisplayMapperFeatureProcessor unable to create LUT %s", lutName.GetCStr()); - } - displayMapperLut = it->second; - } - - void AcesDisplayMapperFeatureProcessor::GetDisplayMapperLut(DisplayMapperLut& displayMapperLut) - { - const AZ::Name acesLutName("AcesLutImage"); - auto it = m_ownedLuts.find(acesLutName); - if (it == m_ownedLuts.end()) - { - InitializeLutImage(acesLutName); - - it = m_ownedLuts.find(acesLutName); - AZ_Assert(it != m_ownedLuts.end(), "AcesDisplayMapperFeatureProcessor unable to create ACES LUT image"); - } - displayMapperLut = it->second; - } - - void AcesDisplayMapperFeatureProcessor::GetLutFromAssetLocation(DisplayMapperAssetLut& displayMapperAssetLut, const AZStd::string& assetPath) - { - Data::AssetId assetId = RPI::AssetUtils::GetAssetIdForProductPath(assetPath.c_str(), RPI::AssetUtils::TraceLevel::Error); - GetLutFromAssetId(displayMapperAssetLut, assetId); - } - - void AcesDisplayMapperFeatureProcessor::GetLutFromAssetId(DisplayMapperAssetLut& displayMapperAssetLut, const AZ::Data::AssetId assetId) - { - if (!assetId.IsValid()) - { - return; - } - - // Check first if this already exists - auto it = m_assetLuts.find(assetId.ToString()); - if (it != m_assetLuts.end()) - { - displayMapperAssetLut = it->second; - return; - } - - // Read the lut which is a .3dl file embedded within an azasset file. - Data::Asset asset = RPI::AssetUtils::LoadAssetById(assetId, RPI::AssetUtils::TraceLevel::Error); - const LookupTableAsset* lutAsset = RPI::GetDataFromAnyAsset(asset); - - if (lutAsset == nullptr) - { - AZ_Error("AcesDisplayMapperFeatureProcessor", false, "Unable to read LUT from asset."); - asset.Release(); - return; - } - - // The first row of numbers in a 3dl file is a number of vertices that partition the space from [0,..1023] - // This assumes that the vertices are evenly spaced apart. Non-uniform spacing is supported by the format, - // but haven't been encountered yet. - uint32_t lutSize = static_cast(lutAsset->m_intervals.size()); - - if (lutSize == 0) - { - AZ_Error("AcesDisplayMapperFeatureProcessor", false, "Lut asset has invalid size."); - asset.Release(); - return; - } - - // The vertices in the file are given as a positive integer value in [0,..4095] and need to be normalized - // and stored into a linear unaligned buffer used to initialize the streaming image. - const float normalizeValue = 4095.0f; - const int kChannels = 4; - const int kChannelBytes = 2; - int bytesPerRow = lutSize * kChannels * kChannelBytes; - int bytesPerSlice = bytesPerRow * lutSize; - - AZStd::vector u16Buffer; - size_t bufferSize = (bytesPerSlice * lutSize) / sizeof(uint16_t); - u16Buffer.resize(bufferSize); - uint16_t* data = u16Buffer.data(); - for (int slice = 0; slice < (int)lutSize; slice++) - { - for (int column = 0; column < (int)lutSize; column++) + for (size_t row = 0; row < lutSize; row++) { - for (int row = 0; row < (int)lutSize; row++) - { - // Index in the LUT texture data - int idx = (column * kChannels) + - (bytesPerRow * row / sizeof(uint16_t)) + - ((bytesPerSlice * slice) / sizeof(uint16_t)); + // Index in the LUT texture data + size_t idx = (column * kChannels) + + ((bytesPerRow * row) / kChannelBytes) + + ((bytesPerSlice * slice) / kChannelBytes); - // Vertices the .3dl file are listed first by increasing slice, then row, and finally column coordinate - // This corresponds to blue, green, and red channels, respectively. - int assetIdx = slice + lutSize * row + (lutSize * lutSize * column); + // Vertices the .3dl file are listed first by increasing slice, then row, and finally column coordinate + // This corresponds to blue, green, and red channels, respectively. + size_t assetIdx = slice + lutSize * row + (lutSize * lutSize * column); - AZ::u64 red = lutAsset->m_values[assetIdx * 3 + 0]; - AZ::u64 green = lutAsset->m_values[assetIdx * 3 + 1]; - AZ::u64 blue = lutAsset->m_values[assetIdx * 3 + 2]; - data[idx + 0] = ConvertFloatToHalf(static_cast(red) / normalizeValue); - data[idx + 1] = ConvertFloatToHalf(static_cast(green) / normalizeValue); - data[idx + 2] = ConvertFloatToHalf(static_cast(blue) / normalizeValue); - data[idx + 3] = 0x3b00; // 1.0 in half - } + AZ::u64 red = lutAsset->m_values[assetIdx * 3 + 0]; + AZ::u64 green = lutAsset->m_values[assetIdx * 3 + 1]; + AZ::u64 blue = lutAsset->m_values[assetIdx * 3 + 2]; + + // The vertices in the file are given as a positive integer value in [0,..4095] and need to be normalized + constexpr float NormalizeValue = 4095.0f; + + u16Buffer[idx + 0] = ConvertFloatToHalf(static_cast(red) / NormalizeValue); + u16Buffer[idx + 1] = ConvertFloatToHalf(static_cast(green) / NormalizeValue); + u16Buffer[idx + 2] = ConvertFloatToHalf(static_cast(blue) / NormalizeValue); + u16Buffer[idx + 3] = 0x3b00; // 1.0 in half } } - - asset.Release(); - - Data::Instance streamingImagePool = RPI::ImageSystemInterface::Get()->GetSystemStreamingPool(); - - RHI::Size imageSize; - imageSize.m_width = static_cast(lutSize); - imageSize.m_height = static_cast(lutSize); - imageSize.m_depth = static_cast(lutSize); - size_t imageDataSize = bytesPerSlice * lutSize; - - Data::Instance lutStreamingImage = RPI::StreamingImage::CreateFromCpuData( - *streamingImagePool, RHI::ImageDimension::Image3D, imageSize, LutFormat, data, imageDataSize); - - AZ_Error("AcesDisplayMapperFeatureProcessor", lutStreamingImage, "Failed to initialize the lut assetId %s.", assetId.ToString().c_str()); - - DisplayMapperAssetLut assetLut; - assetLut.m_lutStreamingImage = lutStreamingImage; - - // Add to the list of LUT asset resources - m_assetLuts.insert(AZStd::pair(assetId.ToString(), assetLut)); - displayMapperAssetLut = assetLut; } - void AcesDisplayMapperFeatureProcessor::InitializeImagePool() + asset.Release(); + + Data::Instance streamingImagePool = RPI::ImageSystemInterface::Get()->GetSystemStreamingPool(); + + RHI::Size imageSize; + imageSize.m_width = static_cast(lutSize); + imageSize.m_height = static_cast(lutSize); + imageSize.m_depth = static_cast(lutSize); + size_t imageDataSize = bytesPerSlice * lutSize; + + Data::Instance lutStreamingImage = RPI::StreamingImage::CreateFromCpuData( + *streamingImagePool, RHI::ImageDimension::Image3D, imageSize, LutFormat, u16Buffer.data(), imageDataSize); + + AZ_Error("AcesDisplayMapperFeatureProcessor", lutStreamingImage, "Failed to initialize the lut assetId %s.", assetId.ToString().c_str()); + + DisplayMapperAssetLut assetLut; + assetLut.m_lutStreamingImage = lutStreamingImage; + + // Add to the list of LUT asset resources + m_assetLuts.insert(AZStd::pair(assetId.ToString(), assetLut)); + displayMapperAssetLut = assetLut; + } + + void AcesDisplayMapperFeatureProcessor::InitializeImagePool() + { + AZ::RHI::Factory& factory = RHI::Factory::Get(); + m_displayMapperImagePool = factory.CreateImagePool(); + m_displayMapperImagePool->SetName(Name("DisplayMapperImagePool")); + + RHI::ImagePoolDescriptor imagePoolDesc = {}; + imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShaderReadWrite; + imagePoolDesc.m_budgetInBytes = ImagePoolBudget; + + RHI::Device* device = RHI::RHISystemInterface::Get()->GetDevice(); + RHI::ResultCode resultCode = m_displayMapperImagePool->Init(*device, imagePoolDesc); + if (resultCode != RHI::ResultCode::Success) { - AZ::RHI::Factory& factory = RHI::Factory::Get(); - m_displayMapperImagePool = factory.CreateImagePool(); - m_displayMapperImagePool->SetName(Name("DisplayMapperImagePool")); - - RHI::ImagePoolDescriptor imagePoolDesc = {}; - imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShaderReadWrite; - imagePoolDesc.m_budgetInBytes = ImagePoolBudget; - - RHI::Device* device = RHI::RHISystemInterface::Get()->GetDevice(); - RHI::ResultCode resultCode = m_displayMapperImagePool->Init(*device, imagePoolDesc); - if (resultCode != RHI::ResultCode::Success) - { - AZ_Error("AcesDisplayMapperFeatureProcessor", false, "Failed to initialize image pool."); - return; - } + AZ_Error("AcesDisplayMapperFeatureProcessor", false, "Failed to initialize image pool."); + return; } + } - void AcesDisplayMapperFeatureProcessor::InitializeLutImage(const AZ::Name& lutName) + void AcesDisplayMapperFeatureProcessor::InitializeLutImage(const AZ::Name& lutName) + { + if (!m_displayMapperImagePool) { - if (!m_displayMapperImagePool) - { - InitializeImagePool(); - } - - DisplayMapperLut lutResource; - lutResource.m_lutImage = RHI::Factory::Get().CreateImage(); - lutResource.m_lutImage->SetName(lutName); - - RHI::ImageInitRequest imageRequest; - imageRequest.m_image = lutResource.m_lutImage.get(); - imageRequest.m_descriptor = RHI::ImageDescriptor::Create3D(RHI::ImageBindFlags::ShaderReadWrite, LutSize, LutSize, LutSize, LutFormat); - RHI::ResultCode resultCode = m_displayMapperImagePool->InitImage(imageRequest); - - if (resultCode != RHI::ResultCode::Success) - { - AZ_Error("AcesDisplayMapperFeatureProcessor", false, "Failed to initialize LUT image."); - return; - } - - lutResource.m_lutImageViewDescriptor = RHI::ImageViewDescriptor::Create(LutFormat, 0, 0); - lutResource.m_lutImageView = lutResource.m_lutImage->GetImageView(lutResource.m_lutImageViewDescriptor); - if (!lutResource.m_lutImageView.get()) - { - AZ_Error("AcesDisplayMapperFeatureProcessor", false, "Failed to initialize LUT image view."); - return; - } - - // Add to the list of lut resources - lutResource.m_lutImageView->SetName(lutName); - m_ownedLuts[lutName] = lutResource; + InitializeImagePool(); } - ShaperParams AcesDisplayMapperFeatureProcessor::GetShaperParameters(ShaperPresetType shaperPreset) + DisplayMapperLut lutResource; + lutResource.m_lutImage = RHI::Factory::Get().CreateImage(); + lutResource.m_lutImage->SetName(lutName); + + RHI::ImageInitRequest imageRequest; + imageRequest.m_image = lutResource.m_lutImage.get(); + static const int LutSize = 32; + imageRequest.m_descriptor = RHI::ImageDescriptor::Create3D(RHI::ImageBindFlags::ShaderReadWrite, LutSize, LutSize, LutSize, LutFormat); + RHI::ResultCode resultCode = m_displayMapperImagePool->InitImage(imageRequest); + + if (resultCode != RHI::ResultCode::Success) { - // Default is a linear shaper with bias 0.0 and scale 1.0. That is, fx = x*1.0 + 0.0 - ShaperParams shaperParams = { ShaperType::Linear, 0.0, 1.f }; - OutputDeviceTransformType outputDeviceTransformType = OutputDeviceTransformType::NumOutputDeviceTransformTypes; - switch (shaperPreset) - { - case ShaperPresetType::None: - break; - case ShaperPresetType::Log2_48_nits: - outputDeviceTransformType = OutputDeviceTransformType::OutputDeviceTransformType_48Nits; - break; - case ShaperPresetType::Log2_1000_nits: - outputDeviceTransformType = OutputDeviceTransformType::OutputDeviceTransformType_1000Nits; - break; - case ShaperPresetType::Log2_2000_nits: - outputDeviceTransformType = OutputDeviceTransformType::OutputDeviceTransformType_2000Nits; - break; - case ShaperPresetType::Log2_4000_nits: - outputDeviceTransformType = OutputDeviceTransformType::OutputDeviceTransformType_4000Nits; - break; - default: - AZ_Error("DisplayMapperPass", false, "Invalid shaper preset type."); - break; - } - if (outputDeviceTransformType < OutputDeviceTransformType::NumOutputDeviceTransformTypes) - { - shaperParams = GetAcesShaperParameters(outputDeviceTransformType); - } - return shaperParams; + AZ_Error("AcesDisplayMapperFeatureProcessor", false, "Failed to initialize LUT image."); + return; } - void AcesDisplayMapperFeatureProcessor::GetDefaultDisplayMapperConfiguration(DisplayMapperConfigurationDescriptor& config) + lutResource.m_lutImageViewDescriptor = RHI::ImageViewDescriptor::Create(LutFormat, 0, 0); + lutResource.m_lutImageView = lutResource.m_lutImage->GetImageView(lutResource.m_lutImageViewDescriptor); + if (!lutResource.m_lutImageView.get()) { - // Default configuration is ACES with LDR color grading LUT disabled. - config.m_operationType = DisplayMapperOperationType::Aces; - config.m_ldrGradingLutEnabled = false; - config.m_ldrColorGradingLut.Release(); + AZ_Error("AcesDisplayMapperFeatureProcessor", false, "Failed to initialize LUT image view."); + return; } - void AcesDisplayMapperFeatureProcessor::RegisterDisplayMapperConfiguration(const DisplayMapperConfigurationDescriptor& config) - { - m_displayMapperConfiguration = config; - } + // Add to the list of lut resources + lutResource.m_lutImageView->SetName(lutName); + m_ownedLuts[lutName] = lutResource; + } - DisplayMapperConfigurationDescriptor AcesDisplayMapperFeatureProcessor::GetDisplayMapperConfiguration() + ShaperParams AcesDisplayMapperFeatureProcessor::GetShaperParameters(ShaperPresetType shaperPreset, float customMinEv, float customMaxEv) + { + // Default is a linear shaper with bias 0.0 and scale 1.0. That is, fx = x*1.0 + 0.0 + ShaperParams shaperParams = { ShaperType::Linear, 0.0, 1.f }; + switch (shaperPreset) { - return m_displayMapperConfiguration; + case ShaperPresetType::None: + break; + case ShaperPresetType::Log2_48Nits: + shaperParams = GetAcesShaperParameters(OutputDeviceTransformType::OutputDeviceTransformType_48Nits); + break; + case ShaperPresetType::Log2_1000Nits: + shaperParams = GetAcesShaperParameters(OutputDeviceTransformType::OutputDeviceTransformType_1000Nits); + break; + case ShaperPresetType::Log2_2000Nits: + shaperParams = GetAcesShaperParameters(OutputDeviceTransformType::OutputDeviceTransformType_2000Nits); + break; + case ShaperPresetType::Log2_4000Nits: + shaperParams = GetAcesShaperParameters(OutputDeviceTransformType::OutputDeviceTransformType_4000Nits); + break; + case ShaperPresetType::LinearCustomRange: + { + // Map the range min exposure - max exposure to 0-1. Convert EV values to linear values here to avoid that work in the shader. + // Shader equation becomes (x - bias) / scale; + constexpr float MediumGray = 0.18f; + const float minValue = MediumGray * powf(2, customMinEv); + const float maxValue = MediumGray * powf(2, customMaxEv); + shaperParams.m_type = ShaperType::Linear; + shaperParams.m_scale = 1.0f / (maxValue - minValue); + shaperParams.m_bias = -minValue * shaperParams.m_scale; + break; } - } // namespace Render -} // namespace AZ + case ShaperPresetType::Log2CustomRange: + shaperParams = GetLog2ShaperParameters(customMinEv, customMaxEv); + break; + case ShaperPresetType::PqSmpteSt2084: + shaperParams.m_type = ShaperType::PqSmpteSt2084; + break; + default: + AZ_Error("DisplayMapperPass", false, "Invalid shaper preset type."); + break; + } + return shaperParams; + } + + void AcesDisplayMapperFeatureProcessor::GetDefaultDisplayMapperConfiguration(DisplayMapperConfigurationDescriptor& config) + { + // Default configuration is ACES with LDR color grading LUT disabled. + config.m_operationType = DisplayMapperOperationType::Aces; + config.m_ldrGradingLutEnabled = false; + config.m_ldrColorGradingLut.Release(); + } + + void AcesDisplayMapperFeatureProcessor::RegisterDisplayMapperConfiguration(const DisplayMapperConfigurationDescriptor& config) + { + m_displayMapperConfiguration = config; + } + + DisplayMapperConfigurationDescriptor AcesDisplayMapperFeatureProcessor::GetDisplayMapperConfiguration() + { + return m_displayMapperConfiguration; + } +} // namespace AZ::Render diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformLutPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformLutPass.cpp index a00f879bca..3b2913aefd 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformLutPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformLutPass.cpp @@ -91,8 +91,8 @@ namespace AZ m_shaderResourceGroup->SetImageView(m_shaderInputLutImageIndex, m_displayMapperLut.m_lutImageView.get()); } - m_shaderResourceGroup->SetConstant(m_shaderInputShaperBiasIndex, m_shaperParams.bias); - m_shaderResourceGroup->SetConstant(m_shaderInputShaperScaleIndex, m_shaperParams.scale); + m_shaderResourceGroup->SetConstant(m_shaderInputShaperBiasIndex, m_shaperParams.m_bias); + m_shaderResourceGroup->SetConstant(m_shaderInputShaperScaleIndex, m_shaperParams.m_scale); } BindPassSrg(context, m_shaderResourceGroup); diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/ApplyShaperLookupTablePass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/ApplyShaperLookupTablePass.cpp index eec511c07a..f4325f0421 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/ApplyShaperLookupTablePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/ApplyShaperLookupTablePass.cpp @@ -109,9 +109,9 @@ namespace AZ { m_shaderResourceGroup->SetImageView(m_shaderInputLutImageIndex, m_lutResource.m_lutStreamingImage->GetImageView()); - m_shaderResourceGroup->SetConstant(m_shaderShaperTypeIndex, m_shaperParams.type); - m_shaderResourceGroup->SetConstant(m_shaderShaperBiasIndex, m_shaperParams.bias); - m_shaderResourceGroup->SetConstant(m_shaderShaperScaleIndex, m_shaperParams.scale); + m_shaderResourceGroup->SetConstant(m_shaderShaperTypeIndex, m_shaperParams.m_type); + m_shaderResourceGroup->SetConstant(m_shaderShaperBiasIndex, m_shaperParams.m_bias); + m_shaderResourceGroup->SetConstant(m_shaderShaperScaleIndex, m_shaperParams.m_scale); } } } diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/BakeAcesOutputTransformLutPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/BakeAcesOutputTransformLutPass.cpp index f7ecfc3461..a8d1b68d63 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/BakeAcesOutputTransformLutPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/BakeAcesOutputTransformLutPass.cpp @@ -94,8 +94,8 @@ namespace AZ m_shaderResourceGroup->SetImageView(m_shaderInputLutImageIndex, m_displayMapperLut.m_lutImageView.get()); - m_shaderResourceGroup->SetConstant(m_shaderInputShaperBiasIndex, m_shaperParams.bias); - m_shaderResourceGroup->SetConstant(m_shaderInputShaperScaleIndex, m_shaperParams.scale); + m_shaderResourceGroup->SetConstant(m_shaderInputShaperBiasIndex, m_shaperParams.m_bias); + m_shaderResourceGroup->SetConstant(m_shaderInputShaperScaleIndex, m_shaperParams.m_scale); } BindPassSrg(context, m_shaderResourceGroup); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/LookModification/LookModificationSettings.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/LookModification/LookModificationSettings.cpp index d2e28e3a68..cbc357db61 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/LookModification/LookModificationSettings.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/LookModification/LookModificationSettings.cpp @@ -26,6 +26,8 @@ namespace AZ seed = TypeHash64(m_overrideStrength, seed); seed = TypeHash64(m_assetId.GetId(), seed); seed = TypeHash64(m_shaperPreset, seed); + seed = TypeHash64(m_customMinExposure, seed); + seed = TypeHash64(m_customMaxExposure, seed); return seed; } @@ -50,6 +52,9 @@ namespace AZ lutBlend.m_intensity = GetColorGradingLutIntensity(); lutBlend.m_overrideStrength = GetColorGradingLutOverride() * alpha; lutBlend.m_assetId = lutAssetId; + lutBlend.m_shaperPreset = GetShaperPresetType(); + lutBlend.m_customMinExposure = GetCustomMinExposure(); + lutBlend.m_customMaxExposure = GetCustomMaxExposure(); target->AddLutBlend(lutBlend); } } @@ -87,6 +92,9 @@ namespace AZ blendItem.m_intensity = GetColorGradingLutIntensity(); blendItem.m_overrideStrength = GetColorGradingLutOverride(); blendItem.m_assetId = GetColorGradingLut(); + blendItem.m_shaperPreset = GetShaperPresetType(); + blendItem.m_customMinExposure = GetCustomMinExposure(); + blendItem.m_customMaxExposure = GetCustomMaxExposure(); m_lutBlendStack.insert(m_lutBlendStack.begin(), blendItem); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/LookModification/LookModificationSettings.h b/Gems/Atom/Feature/Common/Code/Source/PostProcess/LookModification/LookModificationSettings.h index 19bf07010b..d91a029ced 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/LookModification/LookModificationSettings.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/LookModification/LookModificationSettings.h @@ -33,7 +33,10 @@ namespace AZ //! Asset ID of LUT Data::Asset m_assetId; //! Shaper preset type - ShaperPresetType m_shaperPreset = AZ::Render::ShaperPresetType::Log2_48_nits; + ShaperPresetType m_shaperPreset = AZ::Render::ShaperPresetType::Log2_48Nits; + //! When shaper preset is custom, these values set min and max exposure. + float m_customMinExposure = -6.5; + float m_customMaxExposure = 6.5; HashValue64 GetHash(HashValue64 seed) const; }; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp index da680d80cf..8631ae2eb3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp @@ -21,7 +21,7 @@ namespace AZ namespace Render { static const char* const NumSourceLutsShaderVariantOptionName{ "o_numSourceLuts" }; - + RPI::Ptr BlendColorGradingLutsPass::Create(const RPI::PassDescriptor& descriptor) { RPI::Ptr pass = aznew BlendColorGradingLutsPass(descriptor); @@ -151,9 +151,9 @@ namespace AZ { m_shaderResourceGroup->SetImageView(m_shaderInputBlendedLutImageIndex, m_blendedLut.m_lutImageView.get()); m_shaderResourceGroup->SetConstant(m_shaderInputBlendedLutDimensionsIndex, m_blendedLutDimensions); - m_shaderResourceGroup->SetConstant(m_shaderInputBlendedLutShaperTypeIndex, m_blendedLutShaperParams.type); - m_shaderResourceGroup->SetConstant(m_shaderInputBlendededLutShaperBiasIndex, m_blendedLutShaperParams.bias); - m_shaderResourceGroup->SetConstant(m_shaderInputBlendededLutShaperScaleIndex, m_blendedLutShaperParams.scale); + m_shaderResourceGroup->SetConstant(m_shaderInputBlendedLutShaperTypeIndex, m_blendedLutShaperParams.m_type); + m_shaderResourceGroup->SetConstant(m_shaderInputBlendededLutShaperBiasIndex, m_blendedLutShaperParams.m_bias); + m_shaderResourceGroup->SetConstant(m_shaderInputBlendededLutShaperScaleIndex, m_blendedLutShaperParams.m_scale); m_shaderResourceGroup->SetConstant(m_shaderInputWeight0Index, m_weights[0]); m_shaderResourceGroup->SetConstant(m_shaderInputWeight1Index, m_weights[1]); m_shaderResourceGroup->SetConstant(m_shaderInputWeight2Index, m_weights[2]); @@ -163,33 +163,33 @@ namespace AZ if (m_colorGradingLuts[0].m_lutStreamingImage) { m_shaderResourceGroup->SetImageView(m_shaderInputSourceLut1ImageIndex, m_colorGradingLuts[0].m_lutStreamingImage->GetImageView()); - m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut1ShaperTypeIndex, m_colorGradingShaperParams[0].type); - m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut1ShaperBiasIndex, m_colorGradingShaperParams[0].bias); - m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut1ShaperScaleIndex, m_colorGradingShaperParams[0].scale); + m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut1ShaperTypeIndex, m_colorGradingShaperParams[0].m_type); + m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut1ShaperBiasIndex, m_colorGradingShaperParams[0].m_bias); + m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut1ShaperScaleIndex, m_colorGradingShaperParams[0].m_scale); } if (m_colorGradingLuts[1].m_lutStreamingImage) { m_shaderResourceGroup->SetImageView(m_shaderInputSourceLut2ImageIndex, m_colorGradingLuts[1].m_lutStreamingImage->GetImageView()); - m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut2ShaperTypeIndex, m_colorGradingShaperParams[1].type); - m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut2ShaperBiasIndex, m_colorGradingShaperParams[1].bias); - m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut2ShaperScaleIndex, m_colorGradingShaperParams[1].scale); + m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut2ShaperTypeIndex, m_colorGradingShaperParams[1].m_type); + m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut2ShaperBiasIndex, m_colorGradingShaperParams[1].m_bias); + m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut2ShaperScaleIndex, m_colorGradingShaperParams[1].m_scale); } if (m_colorGradingLuts[2].m_lutStreamingImage) { m_shaderResourceGroup->SetImageView(m_shaderInputSourceLut3ImageIndex, m_colorGradingLuts[2].m_lutStreamingImage->GetImageView()); - m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut3ShaperTypeIndex, m_colorGradingShaperParams[2].type); - m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut3ShaperBiasIndex, m_colorGradingShaperParams[2].bias); - m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut3ShaperScaleIndex, m_colorGradingShaperParams[2].scale); + m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut3ShaperTypeIndex, m_colorGradingShaperParams[2].m_type); + m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut3ShaperBiasIndex, m_colorGradingShaperParams[2].m_bias); + m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut3ShaperScaleIndex, m_colorGradingShaperParams[2].m_scale); } if (m_colorGradingLuts[3].m_lutStreamingImage) { m_shaderResourceGroup->SetImageView(m_shaderInputSourceLut4ImageIndex, m_colorGradingLuts[3].m_lutStreamingImage->GetImageView()); - m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut4ShaperTypeIndex, m_colorGradingShaperParams[3].type); - m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut4ShaperBiasIndex, m_colorGradingShaperParams[3].bias); - m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut4ShaperScaleIndex, m_colorGradingShaperParams[3].scale); + m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut4ShaperTypeIndex, m_colorGradingShaperParams[3].m_type); + m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut4ShaperBiasIndex, m_colorGradingShaperParams[3].m_bias); + m_shaderResourceGroup->SetConstant(m_shaderInputSourceLut4ShaperScaleIndex, m_colorGradingShaperParams[3].m_scale); } if (m_shaderResourceGroup->HasShaderVariantKeyFallbackEntry()) @@ -244,7 +244,170 @@ namespace AZ m_blendedLutShaperParams = shaperParams; } + + AZStd::optional BlendColorGradingLutsPass::GetCommonShaperParams() const + { + LookModificationSettings* settings = GetLookModificationSettings(); + if (settings) + { + settings->PrepareLutBlending(); + + ShaperPresetType type = ShaperPresetType::NumShaperTypes; + float customMinExposure = 0.0; + float customMaxExposure = 0.0; + + for (size_t lutIndex = 0; lutIndex < settings->GetLutBlendStackSize(); lutIndex++) + { + LutBlendItem& lutBlendItem = settings->GetLutBlendItem(lutIndex); + + if (lutIndex == 0) + { + type = lutBlendItem.m_shaperPreset; + customMinExposure = lutBlendItem.m_customMinExposure; + customMaxExposure = lutBlendItem.m_customMaxExposure; + } + else if (type != lutBlendItem.m_shaperPreset) + { + // Shapers are different + return AZStd::nullopt; + } + else if (type == ShaperPresetType::LinearCustomRange || type == ShaperPresetType::Log2CustomRange) + { + if (lutBlendItem.m_customMinExposure != customMinExposure || + lutBlendItem.m_customMaxExposure != customMaxExposure) + { + // Shapers are same, but custom exposure for custom type is different. + return AZStd::nullopt; + } + } + } + + // Only calculate shaper params when there's at least one lut blend. + if (settings->GetLutBlendStackSize() > 0) + { + return AcesDisplayMapperFeatureProcessor::GetShaperParameters(type, customMinExposure, customMaxExposure); + } + } + return AZStd::nullopt; + } + void BlendColorGradingLutsPass::CheckLutBlendSettings() + { + LookModificationSettings* settings = GetLookModificationSettings(); + if (settings) + { + settings->PrepareLutBlending(); + + // Early out if the settings have not chanced + HashValue64 hash = settings->GetHash(); + if (hash == m_lutBlendHash) + { + return; + } + m_lutBlendHash = hash; + + m_needToUpdateLut = true; + + // Calculate all the weights and LUT assets and check if there has been a change + // Only the top N LUTs will be blended where N = LookModificationSettings::MaxBlendLuts + // Weight 0 is used for the base color, and the other weights are for the LUTs in increasing priority + size_t numLuts = settings->GetLutBlendStackSize(); + + float intensity[LookModificationSettings::MaxBlendLuts]; + float one_intensity[LookModificationSettings::MaxBlendLuts]; + float over[LookModificationSettings::MaxBlendLuts]; + float one_over[LookModificationSettings::MaxBlendLuts]; + + for (int curLutIndex = 0; curLutIndex < LookModificationSettings::MaxBlendLuts; curLutIndex++) + { + intensity[curLutIndex] = 0.f; + one_intensity[curLutIndex] = 1.f; + over[curLutIndex] = 0.f; + one_over[curLutIndex] = 1.f; + } + + int current = 0; + for (size_t lutIndex = 0; lutIndex < numLuts; lutIndex++) + { + LutBlendItem& lutBlendItem = settings->GetLutBlendItem(lutIndex); + const auto assetId = lutBlendItem.m_assetId.GetId(); + + if (assetId.IsValid()) + { + AcesDisplayMapperFeatureProcessor* dmfp = GetScene()->GetFeatureProcessor(); + dmfp->GetLutFromAssetId(m_colorGradingLuts[current], assetId); + if (!m_colorGradingLuts[current].m_lutStreamingImage) + { + AZ_Warning("BlendColorGradingLutsPass", false, "Unable to load grading LUT from asset %s", + lutBlendItem.m_assetId.ToString().c_str()); + // Skip this LUT + continue; + } + } + + intensity[current] = lutBlendItem.m_intensity; + one_intensity[current] = 1.0f - lutBlendItem.m_intensity; + over[current] = lutBlendItem.m_overrideStrength; + one_over[current] = 1.0f - lutBlendItem.m_overrideStrength; + + m_colorGradingShaperParams[current] = AcesDisplayMapperFeatureProcessor::GetShaperParameters( + lutBlendItem.m_shaperPreset, + lutBlendItem.m_customMinExposure, + lutBlendItem.m_customMaxExposure + ); + + ++current; + if (current == LookModificationSettings::MaxBlendLuts) + { + break; + } + } + + m_weights[0] = 0.f; + // Handle the case where there are no LUTs to be blended, and hence an identity LUT will be generated + if (current == 0) + { + m_weights[0] = 1.f; + // These weights would not be used in the shader in this case, but setting to zero anyways. + for (int lutIndex = 1; lutIndex < LookModificationSettings::MaxBlendLuts + 1; lutIndex++) + { + m_weights[lutIndex] = 0.f; + } + } + else + { + // Compute all the weights + // First compute the weight of the ungraded color value + for (int lutIndex = 0; lutIndex < current; lutIndex++) + { + float weight = one_intensity[lutIndex] * over[lutIndex]; + for (int overrideLutIndex = lutIndex + 1; overrideLutIndex < LookModificationSettings::MaxBlendLuts; overrideLutIndex++) + { + weight *= one_over[overrideLutIndex]; + } + m_weights[0] += weight; + } + // Then compute the weights for the LUTs + for (int weightIndex = 0; weightIndex < current; weightIndex++) + { + m_weights[weightIndex + 1] = intensity[weightIndex] * over[weightIndex]; + for (int lutIndex = weightIndex + 1; lutIndex < LookModificationSettings::MaxBlendLuts; lutIndex++) + { + m_weights[weightIndex + 1] *= one_over[lutIndex]; + } + } + } + + // If the number of source LUTs have changed, the shader variant will need to be updated + if (m_numSourceLuts != current) + { + m_numSourceLuts = current; + m_needToUpdateShaderVariant = true; + } + } + } + + LookModificationSettings* BlendColorGradingLutsPass::GetLookModificationSettings() const { AZ::RPI::Scene* scene = GetScene(); if (scene) @@ -259,109 +422,12 @@ namespace AZ LookModificationSettings* settings = postProcessSettings->GetLookModificationSettings(); if (settings) { - settings->PrepareLutBlending(); - - // Early out if the settings have not chanced - HashValue64 hash = settings->GetHash(); - if (hash == m_lutBlendHash) - { - return; - } - m_lutBlendHash = hash; - - m_needToUpdateLut = true; - - // Calculate all the weights and LUT assets and check if there has been a change - // Only the top N LUTs will be blended where N = LookModificationSettings::MaxBlendLuts - // Weight 0 is used for the base color, and the other weights are for the LUTs in increasing priority - size_t numLuts = settings->GetLutBlendStackSize(); - float intensity[LookModificationSettings::MaxBlendLuts]; - float one_intensity[LookModificationSettings::MaxBlendLuts]; - float over[LookModificationSettings::MaxBlendLuts]; - float one_over[LookModificationSettings::MaxBlendLuts]; - for (int curLutIndex = 0; curLutIndex < LookModificationSettings::MaxBlendLuts; curLutIndex++) - { - intensity[curLutIndex] = 0.f; - one_intensity[curLutIndex] = 1.f; - over[curLutIndex] = 0.f; - one_over[curLutIndex] = 1.f; - } - - int current = 0; - for (size_t lutIndex = 0; lutIndex < numLuts; lutIndex++) - { - LutBlendItem& lutBlendItem = settings->GetLutBlendItem(lutIndex); - auto assetId = lutBlendItem.m_assetId.GetId(); - if (assetId.IsValid()) - { - AcesDisplayMapperFeatureProcessor* dmfp = scene->GetFeatureProcessor(); - dmfp->GetLutFromAssetId(m_colorGradingLuts[lutIndex], assetId); - if (!m_colorGradingLuts[lutIndex].m_lutStreamingImage) - { - AZ_Warning("BlendColorGradingLutsPass", false, "Unable to load grading LUT from asset %s", lutBlendItem.m_assetId.ToString().c_str()); - // Skip this LUT - continue; - } - } - intensity[current] = lutBlendItem.m_intensity; - one_intensity[current] = 1.f - intensity[lutIndex]; - over[current] = lutBlendItem.m_overrideStrength; - one_over[current] = 1.f - over[lutIndex]; - m_colorGradingLutAssets[current] = lutBlendItem.m_assetId; - m_colorGradingShaperPresets[current] = lutBlendItem.m_shaperPreset; - m_colorGradingShaperParams[current] = AcesDisplayMapperFeatureProcessor::GetShaperParameters(m_colorGradingShaperPresets[lutIndex]); - current++; - if (current == LookModificationSettings::MaxBlendLuts) - { - break; - } - } - - m_weights[0] = 0.f; - // Handle the case where there are no LUTs to be blended, and hence an identity LUT will be generated - if (current == 0) - { - m_weights[0] = 1.f; - // These weights would not be used in the shader in this case, but setting to zero anyways. - for (int lutIndex = 1; lutIndex < LookModificationSettings::MaxBlendLuts + 1; lutIndex++) - { - m_weights[lutIndex] = 0.f; - } - } - else - { - // Compute all the weights - // First compute the weight of the ungraded color value - for (int lutIndex = 0; lutIndex < LookModificationSettings::MaxBlendLuts; lutIndex++) - { - float weight = one_intensity[lutIndex] * over[lutIndex]; - for (int overrideLutIndex = lutIndex + 1; overrideLutIndex < LookModificationSettings::MaxBlendLuts; overrideLutIndex++) - { - weight *= one_over[overrideLutIndex]; - } - m_weights[0] += weight; - } - // Then compute the weights for the LUTs - for (int weightIndex = 0; weightIndex < LookModificationSettings::MaxBlendLuts; weightIndex++) - { - m_weights[weightIndex + 1] = intensity[weightIndex] * over[weightIndex]; - for (int lutIndex = weightIndex + 1; lutIndex < LookModificationSettings::MaxBlendLuts; lutIndex++) - { - m_weights[weightIndex + 1] *= one_over[lutIndex]; - } - } - } - - // If the number of source LUTs have changed, the shader variant will need to be updated - if (m_numSourceLuts != current) - { - m_numSourceLuts = current; - m_needToUpdateShaderVariant = true; - } + return settings; } } } } + return nullptr; } } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.h index d0e6aad4aa..7b37d2c4ec 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.h @@ -47,6 +47,7 @@ namespace AZ static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); void SetShaperParameters(const ShaperParams& shaperParams); + AZStd::optional GetCommonShaperParams() const; private: explicit BlendColorGradingLutsPass(const RPI::PassDescriptor& descriptor); @@ -66,6 +67,7 @@ namespace AZ void ReleaseLutImage(); void CheckLutBlendSettings(); + LookModificationSettings* GetLookModificationSettings() const; bool m_resourcesInitialized = false; @@ -111,8 +113,6 @@ namespace AZ AZStd::array m_blendedLutDimensions; float m_weights[LookModificationSettings::MaxBlendLuts + 1]; // The first index is reserved for the weight of the non color graded value - Data::Asset m_colorGradingLutAssets[LookModificationSettings::MaxBlendLuts]; - ShaperPresetType m_colorGradingShaperPresets[LookModificationSettings::MaxBlendLuts]; Render::ShaperParams m_colorGradingShaperParams[LookModificationSettings::MaxBlendLuts]; Render::DisplayMapperAssetLut m_colorGradingLuts[LookModificationSettings::MaxBlendLuts]; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp index 40f8f8355d..85c45de96b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp @@ -9,11 +9,15 @@ #include #include #include +#include +#include #include #include #include +#include + #include #include #include @@ -22,6 +26,22 @@ namespace AZ { namespace Render { + AZ_CVAR(uint8_t, + r_lutSampleQuality, + 0, + [](const uint8_t& value) + { + auto passes = RPI::PassSystem::Get()->FindPasses(RPI::PassClassFilter()); + for (auto* pass : passes) + { + LookModificationCompositePass* lookModPass = azrtti_cast(pass); + lookModPass->SetSampleQuality(LookModificationCompositePass::SampleQuality(value)); + } + }, + ConsoleFunctorFlags::Null, + "This can be increased to deal with particularly tricky luts. Range (0-2). 0 (default) - Standard linear sampling. 1 - 7 tap b-spline sampling. 2 - 19 tap b-spline sampling." + ); + RPI::Ptr LookModificationCompositePass::Create(const RPI::PassDescriptor& descriptor) { RPI::Ptr pass = aznew LookModificationCompositePass(descriptor); @@ -30,8 +50,6 @@ namespace AZ LookModificationCompositePass::LookModificationCompositePass(const RPI::PassDescriptor& descriptor) : AZ::RPI::FullscreenTrianglePass(descriptor) - , m_exposureShaderVariantOptionName(ExposureShaderVariantOptionName) - , m_colorGradingShaderVariantOptionName(ColorGradingShaderVariantOptionName) { } @@ -60,19 +78,38 @@ namespace AZ { AZ_Assert(m_shader != nullptr, "LookModificationCompositePass %s has a null shader when calling InitializeShaderVariant.", GetPathName().GetCStr()); - AZStd::vector exposureVariationTypes = { AZ::Name("true"), AZ::Name("false") }; - AZStd::vector colorGradingVariationTypes = { AZ::Name("true"), AZ::Name("false") }; + struct OptionSettings + { + AZ::Name m_enableExposureControl; + AZ::Name m_enableColorGrading; + RPI::ShaderOptionValue m_lutSampleQuality; - auto exposureVariationTypeCount = exposureVariationTypes.size(); - auto totalVariationCount = exposureVariationTypes.size() * colorGradingVariationTypes.size(); + OptionSettings(const char* enableExposureControl, const char* enableColorGrading, SampleQuality sampleQuality) + : m_enableExposureControl(Name(enableExposureControl)) + , m_enableColorGrading(Name(enableColorGrading)) + , m_lutSampleQuality(RPI::ShaderOptionValue(sampleQuality)) + {} + }; + + AZStd::vector options = + { + { "false", "false", SampleQuality::Linear }, + { "true", "false", SampleQuality::Linear }, + { "false", "true", SampleQuality::Linear }, + { "false", "true", SampleQuality::BSpline7Tap }, + { "false", "true", SampleQuality::BSpline19Tap }, + { "true", "true", SampleQuality::Linear }, + { "true", "true", SampleQuality::BSpline7Tap }, + { "true", "true", SampleQuality::BSpline19Tap }, + }; // Caching all pipeline state for each shader variation for performance reason. - for (auto shaderVariantIndex = 0; shaderVariantIndex < totalVariationCount; ++shaderVariantIndex) + for (auto shaderVariantIndex = 0; shaderVariantIndex < options.size(); ++shaderVariantIndex) { auto shaderOption = m_shader->CreateShaderOptionGroup(); - shaderOption.SetValue(m_exposureShaderVariantOptionName, exposureVariationTypes[shaderVariantIndex % exposureVariationTypeCount]); - shaderOption.SetValue(m_colorGradingShaderVariantOptionName, colorGradingVariationTypes[shaderVariantIndex / exposureVariationTypeCount]); - + shaderOption.SetValue(m_exposureShaderVariantOptionName, options.at(shaderVariantIndex).m_enableExposureControl); + shaderOption.SetValue(m_colorGradingShaderVariantOptionName, options.at(shaderVariantIndex).m_enableColorGrading); + shaderOption.SetValue(m_lutSampleQualityShaderVariantOptionName, options.at(shaderVariantIndex).m_lutSampleQuality); PreloadShaderVariant(m_shader, shaderOption, GetRenderAttachmentConfiguration(), GetMultisampleState()); } @@ -173,9 +210,9 @@ namespace AZ { m_shaderResourceGroup->SetImageView(m_shaderColorGradingLutImageIndex, m_blendedColorGradingLut.m_lutImageView.get()); - m_shaderResourceGroup->SetConstant(m_shaderColorGradingShaperTypeIndex, m_colorGradingShaperParams.type); - m_shaderResourceGroup->SetConstant(m_shaderColorGradingShaperBiasIndex, m_colorGradingShaperParams.bias); - m_shaderResourceGroup->SetConstant(m_shaderColorGradingShaperScaleIndex, m_colorGradingShaperParams.scale); + m_shaderResourceGroup->SetConstant(m_shaderColorGradingShaperTypeIndex, m_colorGradingShaperParams.m_type); + m_shaderResourceGroup->SetConstant(m_shaderColorGradingShaperBiasIndex, m_colorGradingShaperParams.m_bias); + m_shaderResourceGroup->SetConstant(m_shaderColorGradingShaperScaleIndex, m_colorGradingShaperParams.m_scale); } } @@ -192,7 +229,8 @@ namespace AZ // Decide which shader to use. shaderOption.SetValue(m_exposureShaderVariantOptionName, m_exposureControlEnabled ? AZ::Name("true") : AZ::Name("false")); shaderOption.SetValue(m_colorGradingShaderVariantOptionName, m_colorGradingLutEnabled ? AZ::Name("true") : AZ::Name("false")); - + shaderOption.SetValue(m_lutSampleQualityShaderVariantOptionName, RPI::ShaderOptionValue(m_sampleQuality)); + UpdateShaderVariant(shaderOption); m_needToUpdateShaderVariant = false; @@ -218,5 +256,12 @@ namespace AZ { m_colorGradingShaperParams = shaperParams; } + + void LookModificationCompositePass::SetSampleQuality(SampleQuality sampleQuality) + { + m_sampleQuality = sampleQuality; + m_needToUpdateShaderVariant = true; + } + } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.h index 49a0e88a54..b269225445 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.h @@ -30,8 +30,6 @@ namespace AZ namespace Render { static const char* const LookModificationTransformPassTemplateName{ "LookModificationTransformTemplate" }; - static const char* const ExposureShaderVariantOptionName{ "o_enableExposureControlFeature" }; - static const char* const ColorGradingShaderVariantOptionName{ "o_enableColorGradingLut" }; /** * The look modification composite pass. If color grading LUTs are enabled, this pass will apply the blended LUT. @@ -43,6 +41,14 @@ namespace AZ public: AZ_RTTI(LookModificationCompositePass, "{D7DF3E8A-B642-4D51-ABC2-ADB2B60FCE1D}", AZ::RPI::FullscreenTrianglePass); AZ_CLASS_ALLOCATOR(LookModificationCompositePass, SystemAllocator, 0); + + enum class SampleQuality : uint8_t + { + Linear = 0, + BSpline7Tap = 1, + BSpline19Tap = 2, + }; + virtual ~LookModificationCompositePass() = default; //! Creates a LookModificationPass @@ -54,6 +60,8 @@ namespace AZ //! Set shaper parameters void SetShaperParameters(const ShaperParams& shaperParams); + void SetSampleQuality(SampleQuality sampleQuality); + protected: LookModificationCompositePass(const RPI::PassDescriptor& descriptor); @@ -76,11 +84,15 @@ namespace AZ bool m_exposureControlEnabled = false; bool m_colorGradingLutEnabled = false; + SampleQuality m_sampleQuality = SampleQuality::Linear; + Render::DisplayMapperLut m_blendedColorGradingLut; Render::ShaperParams m_colorGradingShaperParams; - const AZ::Name m_exposureShaderVariantOptionName; - const AZ::Name m_colorGradingShaderVariantOptionName; + const AZ::Name m_exposureShaderVariantOptionName{ "o_enableExposureControlFeature" }; + const AZ::Name m_colorGradingShaderVariantOptionName{ "o_enableColorGradingLut" }; + const AZ::Name m_lutSampleQualityShaderVariantOptionName{ "o_lutSampleQuality" }; + bool m_needToUpdateShaderVariant = true; RHI::ShaderInputNameIndex m_shaderColorGradingLutImageIndex = "m_gradingLut"; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationTransformPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationTransformPass.cpp index cde7d6586b..e33a63a4bb 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationTransformPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationTransformPass.cpp @@ -47,29 +47,32 @@ namespace AZ swapChainFormat = m_swapChainAttachmentBinding->m_attachment->GetTransientImageDescriptor().m_imageDescriptor.m_format; } - if (m_displayBufferFormat != swapChainFormat) + // Update the children passes + RPI::Ptr blendPass = FindChildPass(); + if (blendPass) { - m_displayBufferFormat = swapChainFormat; - m_outputDeviceTransformType = AcesDisplayMapperFeatureProcessor::GetOutputDeviceTransformType(m_displayBufferFormat); - m_shaperParams = GetAcesShaperParameters(m_outputDeviceTransformType); - - // Update the children passes - for (const AZ::RPI::Ptr& child : m_children) + auto commonShaperParams = blendPass->GetCommonShaperParams(); + if (commonShaperParams) { - BlendColorGradingLutsPass* blendPass = azrtti_cast(child.get()); - if (blendPass) - { - blendPass->SetShaperParameters(m_shaperParams); - continue; - } - LookModificationCompositePass* compositePass = azrtti_cast(child.get()); - if (compositePass) - { - compositePass->SetShaperParameters(m_shaperParams); - continue; - } + m_shaperParams = *commonShaperParams; + } + else + { + // Mix of shapers used, so shape them based on the output transform type. + m_displayBufferFormat = swapChainFormat; + m_outputDeviceTransformType = AcesDisplayMapperFeatureProcessor::GetOutputDeviceTransformType(m_displayBufferFormat); + m_shaperParams = GetAcesShaperParameters(m_outputDeviceTransformType); + } + + blendPass->SetShaperParameters(m_shaperParams); + + RPI::Ptr compositePass = FindChildPass(); + if (compositePass) + { + compositePass->SetShaperParameters(m_shaperParams); } } + ParentPass::FrameBeginInternal(params); } } // namespace Render diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h index 7e341d43c3..36994ec03b 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h @@ -64,6 +64,9 @@ namespace AZ //! Find a child pass with a matching name and returns it. Return nullptr if none found. Ptr FindChildPass(const Name& passName) const; + + template + Ptr FindChildPass() const; //! Searches the tree for the first pass that has same pass name (Depth-first search). Return nullptr if none found. Ptr FindPassByNameRecursive(const Name& passName) const; @@ -132,5 +135,20 @@ namespace AZ // Generates child passes from source PassTemplate void CreatePassesFromTemplate(); }; + + template + inline Ptr ParentPass::FindChildPass() const + { + for (const Ptr& child : m_children) + { + PassType* pass = azrtti_cast(child.get()); + if (pass) + { + return pass; + } + } + return {}; + } + } // namespace RPI } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/LookModification/LookModificationComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/LookModification/LookModificationComponentConfig.h index 5ee348a052..0bf9e752a2 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/LookModification/LookModificationComponentConfig.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/LookModification/LookModificationComponentConfig.h @@ -39,6 +39,11 @@ namespace AZ void CopySettingsTo(LookModificationSettingsInterface* settings); bool ArePropertiesReadOnly() const { return !m_enabled; } + + bool IsUsingCustomShaper() const { + return m_shaperPresetType == ShaperPresetType::LinearCustomRange + || m_shaperPresetType == ShaperPresetType::Log2CustomRange; + } }; } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/EditorLookModificationComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/EditorLookModificationComponent.cpp index a7dfb76e95..310762863a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/EditorLookModificationComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/LookModification/EditorLookModificationComponent.cpp @@ -48,33 +48,44 @@ namespace AZ &LookModificationComponentConfig::m_enabled, "Enable look modification", "Enable look modification.") - ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &LookModificationComponentConfig::m_colorGradingLut, "Color Grading LUT", "Color grading LUT") - ->ClassElement(Edit::ClassElements::EditorData, "") ->DataElement(Edit::UIHandlers::ComboBox, - &LookModificationComponentConfig::m_shaperPresetType, - "Shaper Type", - "Shaper Type.") - ->EnumAttribute(ShaperPresetType::None, "None") - ->EnumAttribute(ShaperPresetType::Log2_48_nits, "Log2_48_nits") - ->EnumAttribute(ShaperPresetType::Log2_1000_nits, "Log2_1000_nits") - ->EnumAttribute(ShaperPresetType::Log2_2000_nits, "Log2_2000_nits") - ->EnumAttribute(ShaperPresetType::Log2_4000_nits, "Log2_4000_nits") - ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - + &LookModificationComponentConfig::m_shaperPresetType, "Shaper Type", "Shaper Type.") + ->EnumAttribute(ShaperPresetType::None, "None") + ->EnumAttribute(ShaperPresetType::LinearCustomRange, "Linear Custom Range") + ->EnumAttribute(ShaperPresetType::Log2_48Nits, "Log2 48 nits") + ->EnumAttribute(ShaperPresetType::Log2_1000Nits, "Log2 1000 nits") + ->EnumAttribute(ShaperPresetType::Log2_2000Nits, "Log2 2000 nits") + ->EnumAttribute(ShaperPresetType::Log2_4000Nits, "Log2 4000 nits") + ->EnumAttribute(ShaperPresetType::Log2CustomRange, "Log2 Custom Range") + ->EnumAttribute(ShaperPresetType::PqSmpteSt2084, "PQ (SMPTE ST 2084)") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::EntireTree) + ->DataElement(AZ::Edit::UIHandlers::Slider, &LookModificationComponentConfig::m_customMinExposure, "Minimum Exposure", "The minimum exposure this LUT supports. Values smaller than this will be clamped to 0.") + ->Attribute(AZ::Edit::Attributes::Min, -50.0f) + ->Attribute(AZ::Edit::Attributes::Max, 0.0f) + ->Attribute(AZ::Edit::Attributes::SoftMin, -20.0f) + ->Attribute(AZ::Edit::Attributes::SoftMax, 0.0f) + ->Attribute(Edit::Attributes::Visibility, &LookModificationComponentConfig::IsUsingCustomShaper) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->DataElement(AZ::Edit::UIHandlers::Slider, &LookModificationComponentConfig::m_customMaxExposure, "Maximum Exposure", "The maximum exposure this LUT supports. Values larger than this will be clamped.") + ->Attribute(AZ::Edit::Attributes::Min, 0.0f) + ->Attribute(AZ::Edit::Attributes::Max, 50.0f) + ->Attribute(AZ::Edit::Attributes::SoftMin, 0.0f) + ->Attribute(AZ::Edit::Attributes::SoftMax, 20.0f) + ->Attribute(Edit::Attributes::Visibility, &LookModificationComponentConfig::IsUsingCustomShaper) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement(AZ::Edit::UIHandlers::Slider, &LookModificationComponentConfig::m_colorGradingLutIntensity, "LUT Intensity", "Blend intensity of this LUT.") - ->Attribute(AZ::Edit::Attributes::Min, 0.0f) - ->Attribute(AZ::Edit::Attributes::Max, 1.0f) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - ->Attribute(Edit::Attributes::ReadOnly, &LookModificationComponentConfig::ArePropertiesReadOnly) - + ->Attribute(AZ::Edit::Attributes::Min, 0.0f) + ->Attribute(AZ::Edit::Attributes::Max, 1.0f) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->Attribute(Edit::Attributes::ReadOnly, &LookModificationComponentConfig::ArePropertiesReadOnly) ->DataElement(AZ::Edit::UIHandlers::Slider, &LookModificationComponentConfig::m_colorGradingLutOverride, "LUT Override", "Blend intensity of this LUT.") - ->Attribute(AZ::Edit::Attributes::Min, 0.0f) - ->Attribute(AZ::Edit::Attributes::Max, 1.0f) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) - ->Attribute(Edit::Attributes::ReadOnly, &LookModificationComponentConfig::ArePropertiesReadOnly) + ->Attribute(AZ::Edit::Attributes::Min, 0.0f) + ->Attribute(AZ::Edit::Attributes::Max, 1.0f) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->Attribute(Edit::Attributes::ReadOnly, &LookModificationComponentConfig::ArePropertiesReadOnly) // Overrides ->ClassElement(AZ::Edit::ClassElements::Group, "Overrides") diff --git a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp index 18ee0285b8..b4ae490e2c 100644 --- a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp +++ b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp @@ -10,9 +10,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -306,7 +304,10 @@ namespace SceneBuilder if (itr != request.m_jobDescription.m_jobParameters.end() && itr->second == "true") { - BuildDebugSceneGraph(outputFolder.c_str(), productList, scene); + AZStd::string productName; + AzFramework::StringFunc::Path::GetFullFileName(scene->GetSourceFilename().c_str(), productName); + AzFramework::StringFunc::Path::ReplaceExtension(productName, "dbgsg"); + AZ::SceneAPI::Utilities::DebugOutput::BuildDebugSceneGraph(outputFolder.c_str(), productList, scene, productName); } AZ_TracePrintf(Utilities::LogWindow, "Collecting and registering products.\n"); @@ -371,66 +372,4 @@ namespace SceneBuilder return id; } - - void WriteAndLog(AZ::IO::SystemFile& dbgFile, const char* strToWrite) - { - AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "%s", strToWrite); - dbgFile.Write(strToWrite, strlen(strToWrite)); - dbgFile.Write("\n", strlen("\n")); - - } - - void SceneBuilderWorker::BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr& scene) const - { - const int debugSceneGraphVersion = 1; - AZStd::string productName, debugSceneFile; - - AzFramework::StringFunc::Path::GetFullFileName(scene->GetSourceFilename().c_str(), productName); - AzFramework::StringFunc::Path::ReplaceExtension(productName, "dbgsg"); - AzFramework::StringFunc::Path::ConstructFull(outputFolder, productName.c_str(), debugSceneFile); - AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "outputFolder %s, name %s.\n", outputFolder, productName.c_str()); - - AZ::IO::SystemFile dbgFile; - if (dbgFile.Open(debugSceneFile.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY)) - { - WriteAndLog(dbgFile, AZStd::string::format("ProductName: %s", productName.c_str()).c_str()); - WriteAndLog(dbgFile, AZStd::string::format("debugSceneGraphVersion: %d", debugSceneGraphVersion).c_str()); - WriteAndLog(dbgFile, scene->GetName().c_str()); - - const AZ::SceneAPI::Containers::SceneGraph& sceneGraph = scene->GetGraph(); - auto names = sceneGraph.GetNameStorage(); - auto content = sceneGraph.GetContentStorage(); - auto pairView = AZ::SceneAPI::Containers::Views::MakePairView(names, content); - auto view = AZ::SceneAPI::Containers::Views::MakeSceneGraphDownwardsView< - AZ::SceneAPI::Containers::Views::BreadthFirst>( - sceneGraph, sceneGraph.GetRoot(), pairView.cbegin(), true); - - for (auto&& viewIt : view) - { - if (viewIt.second == nullptr) - { - continue; - } - - AZ::SceneAPI::DataTypes::IGraphObject* graphObject = const_cast(viewIt.second.get()); - - WriteAndLog(dbgFile, AZStd::string::format("Node Name: %s", viewIt.first.GetName()).c_str()); - WriteAndLog(dbgFile, AZStd::string::format("Node Path: %s", viewIt.first.GetPath()).c_str()); - WriteAndLog(dbgFile, AZStd::string::format("Node Type: %s", graphObject->RTTI_GetTypeName()).c_str()); - - AZ::SceneAPI::Utilities::DebugOutput debugOutput; - viewIt.second->GetDebugOutput(debugOutput); - - if (!debugOutput.GetOutput().empty()) - { - WriteAndLog(dbgFile, debugOutput.GetOutput().c_str()); - } - } - dbgFile.Close(); - - static const AZ::Data::AssetType dbgSceneGraphAssetType("{07F289D1-4DC7-4C40-94B4-0A53BBCB9F0B}"); - productList.AddProduct(productName, AZ::Uuid::CreateName(productName.c_str()), dbgSceneGraphAssetType, - AZStd::nullopt, AZStd::nullopt); - } - } } // namespace SceneBuilder diff --git a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.h b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.h index 895bcf5672..3ecc657a3a 100644 --- a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.h +++ b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.h @@ -55,9 +55,6 @@ namespace SceneBuilder void PopulateProductDependencies(const AZ::SceneAPI::Events::ExportProduct& exportProduct, const char* watchFolder, AssetBuilderSDK::JobProduct& jobProduct) const; protected: - - void BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr& scene) const; - bool LoadScene(AZStd::shared_ptr& result, const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Header.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Header.jinja index 00c23b9f35..d743a95ee1 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Header.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Header.jinja @@ -77,7 +77,7 @@ public: \ {% if Class.attrib['GraphEntryPoint'] is defined %} bool IsEntryPoint() const override { return {%if Class.attrib['GraphEntryPoint'] == "True" %}true{%else%}false{%endif%}; } \ {% endif %} public: \ - friend struct ::{{ className | replace(' ','') }}Property; + friend struct {% if attribute_Namespace is not defined %}::{% endif %}{{ className | replace(' ','') }}Property; // Helpers for easily accessing properties and slots struct {{ className | replace(' ','') }}Property diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Source.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Source.jinja index 205e6ebd8c..6a381398c1 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Source.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasGrammar_Source.jinja @@ -26,6 +26,20 @@ SPDX-License-Identifier: Apache-2.0 OR MIT #include "{{ xml.attrib['Include'] }}" {% for Class in xml.iter('Class') %} + +{% set attribute_Namespace = undefined %} +{%- if Class.attrib['Namespace'] is defined %} +{% if Class.attrib['Namespace'] != "None" %} +{% set attribute_Namespace = Class.attrib['Namespace'] %} +{% endif %} +{% endif %} + +{% if attribute_Namespace is defined %} +namespace {{attribute_Namespace}} +{ +{% endif %} + + void {{ Class.attrib['QualifiedName'] }}::ConfigureSlots() { {% if Class.attrib['Base'] is defined %} @@ -269,7 +283,12 @@ void {{ Class.attrib['QualifiedName'] }}::Reflect(AZ::ReflectContext* context) return datumValue ? *datumValue : {{ Property.attrib['Type'] }}(); } - {% endfor %} + +{% if attribute_Namespace is defined %} +} +{% endif %} + + {% endfor %} {% endfor %} diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 1c0409ceec..e45b7075f0 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -643,7 +643,7 @@ try { defaultValue = jenkinsParameter['default_value'] // Use last run's value as default value so we can save values in different Jenkins environment if (jenkinsParameter['use_last_run_value']?.toBoolean()) { - defaultValue = params."$jenkinsParameter['parameter_name']" ?: jenkinsParameter['default_value'] + defaultValue = params."${jenkinsParameter['parameter_name']}" ?: jenkinsParameter['default_value'] } switch (jenkinsParameter['parameter_type']) { case 'string':