From b000b5fab3247489d568e5d2b75f70d8a17cddd4 Mon Sep 17 00:00:00 2001 From: Neil Widmaier Date: Fri, 5 Nov 2021 12:38:57 -0700 Subject: [PATCH 1/9] Adding P0 HDRi Skybox test Signed-off-by: Neil Widmaier --- .../Gem/PythonTests/Atom/TestSuite_Main.py | 4 + ...ra_AtomEditorComponents_HDRiSkyboxAdded.py | 159 ++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py index 52e7ec993e..6fc18f9043 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py @@ -53,6 +53,10 @@ class TestAutomation(EditorTestSuite): class AtomEditorComponents_HDRColorGradingAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_HDRColorGradingAdded as test_module + @pytest.mark.test_case_id("C32078116") + class AtomEditorComponents_HDRiSkyboxAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_HDRiSkyboxAdded as test_module + @pytest.mark.test_case_id("C32078117") class AtomEditorComponents_LightAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_LightAdded as test_module diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py new file mode 100644 index 0000000000..92ac98f3ec --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py @@ -0,0 +1,159 @@ +""" +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 +""" + +class Tests: + creation_undo = ( + "UNDO Entity creation success", + "UNDO Entity creation failed") + creation_redo = ( + "REDO Entity creation success", + "REDO Entity creation failed") + hdri_skybox_entity_creation = ( + "HDRi Skybox successfully created", + "HDRi Skybox failed to be created") + hdri_skybox_entity_component_added = ( + "Entity has a HDRi Skybox component", + "Entity failed to find HDRi Skybox component") + enter_game_mode = ( + "Entered game mode", + "Failed to enter game mode") + exit_game_mode = ( + "Exited game mode", + "Couldn't exit game mode") + is_visible = ( + "Entity is visible", + "Entity was not visible") + is_hidden = ( + "Entity is hidden", + "Entity was not hidden") + entity_deleted = ( + "Entity deleted", + "Entity was not deleted") + deletion_undo = ( + "UNDO deletion success", + "UNDO deletion failed") + deletion_redo = ( + "REDO deletion success", + "REDO deletion failed") + + +def AtomEditorComponents_HDRiSkybox_AddedToEntity(): + """ + Summary: + Tests the HDRi Skybox component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a HDRi Skybox with no components. + 2) Add a HDRi Skybox component to HDRi Skybox. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Enter/Exit game mode. + 6) Test IsHidden. + 7) Test IsVisible. + 8) Delete HDRi Skybox. + 9) UNDO deletion. + 10) REDO deletion. + 11) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper + from Atom.atom_utils.atom_constants import AtomComponentProperties + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + TestHelper.init_idle() + TestHelper.open_level("", "Base") + + # Test steps begin. + # 1. Create a HDRi Skybox with no components. + hdri_skybox_entity = EditorEntity.create_editor_entity( + AtomComponentProperties.hdri_skybox()) + Report.critical_result(Tests.hdri_skybox_entity_creation, + hdri_skybox_entity.exists()) + + # 2. Add a HDRi Skybox component to HDRi Skybox. + hdri_skybox_component = hdri_skybox_entity.add_component( + AtomComponentProperties.hdri_skybox()) + Report.critical_result( + Tests.hdri_skybox_entity_component_added, + hdri_skybox_entity.has_component(AtomComponentProperties.hdri_skybox())) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not hdri_skybox_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, hdri_skybox_entity.exists()) + + # 5. Enter/Exit game mode. + TestHelper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + TestHelper.exit_game_mode(Tests.exit_game_mode) + + # 6. Test IsHidden. + hdri_skybox_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, hdri_skybox_entity.is_hidden() is True) + + # 7. Test IsVisible. + hdri_skybox_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, hdri_skybox_entity.is_visible() is True) + + # 8. Delete hdri_skybox entity. + hdri_skybox_entity.delete() + Report.result(Tests.entity_deleted, not hdri_skybox_entity.exists()) + + # 9. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, hdri_skybox_entity.exists()) + + # 10. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not hdri_skybox_entity.exists()) + + # 11. Look for errors or asserts. + TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + for error_info in error_tracer.errors: + Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") + for assert_info in error_tracer.asserts: + Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}") + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_HDRiSkybox_AddedToEntity) From c886996bd7b4ac7abb01a5208c51375001d050cd Mon Sep 17 00:00:00 2001 From: Neil Widmaier Date: Fri, 5 Nov 2021 14:15:11 -0700 Subject: [PATCH 2/9] fixing typos and formatting Signed-off-by: Neil Widmaier --- .../hydra_AtomEditorComponents_HDRiSkyboxAdded.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py index 92ac98f3ec..835026a135 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py @@ -5,6 +5,7 @@ For complete copyright and license terms please see the LICENSE at the root of t SPDX-License-Identifier: Apache-2.0 OR MIT """ + class Tests: creation_undo = ( "UNDO Entity creation success", @@ -16,7 +17,7 @@ class Tests: "HDRi Skybox successfully created", "HDRi Skybox failed to be created") hdri_skybox_entity_component_added = ( - "Entity has a HDRi Skybox component", + "Entity has an HDRi Skybox component", "Entity failed to find HDRi Skybox component") enter_game_mode = ( "Entered game mode", @@ -55,8 +56,8 @@ def AtomEditorComponents_HDRiSkybox_AddedToEntity(): Creation and deletion undo/redo should also work. Test Steps: - 1) Create a HDRi Skybox with no components. - 2) Add a HDRi Skybox component to HDRi Skybox. + 1) Create an HDRi Skybox with no components. + 2) Add an HDRi Skybox component to HDRi Skybox. 3) UNDO the entity creation and component addition. 4) REDO the entity creation and component addition. 5) Enter/Exit game mode. @@ -83,13 +84,13 @@ def AtomEditorComponents_HDRiSkybox_AddedToEntity(): TestHelper.open_level("", "Base") # Test steps begin. - # 1. Create a HDRi Skybox with no components. + # 1. Create an HDRi Skybox with no components. hdri_skybox_entity = EditorEntity.create_editor_entity( AtomComponentProperties.hdri_skybox()) Report.critical_result(Tests.hdri_skybox_entity_creation, hdri_skybox_entity.exists()) - # 2. Add a HDRi Skybox component to HDRi Skybox. + # 2. Add an HDRi Skybox component to HDRi Skybox. hdri_skybox_component = hdri_skybox_entity.add_component( AtomComponentProperties.hdri_skybox()) Report.critical_result( From 9d656005c48b85778d26e048539c03e5b555fe80 Mon Sep 17 00:00:00 2001 From: Neil Widmaier Date: Fri, 5 Nov 2021 14:48:15 -0700 Subject: [PATCH 3/9] Adding fmt comments Signed-off-by: Neil Widmaier --- .../tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py index 835026a135..0576de0a62 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py @@ -6,6 +6,9 @@ SPDX-License-Identifier: Apache-2.0 OR MIT """ +#fmt: off + + class Tests: creation_undo = ( "UNDO Entity creation success", @@ -42,6 +45,9 @@ class Tests: "REDO deletion failed") +#fmt: on + + def AtomEditorComponents_HDRiSkybox_AddedToEntity(): """ Summary: From 98f589745b1aa6a911b49812c7ec920f8b52f2fe Mon Sep 17 00:00:00 2001 From: Neil Widmaier Date: Fri, 12 Nov 2021 10:55:52 -0800 Subject: [PATCH 4/9] Adding step to assign a cubemap Signed-off-by: Neil Widmaier --- .../Gem/PythonTests/Atom/TestSuite_Main.py | 16 ++++++++ ...ra_AtomEditorComponents_HDRiSkyboxAdded.py | 41 ++++++++++++------- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py index 6fc18f9043..b4de39ae98 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py @@ -29,6 +29,10 @@ class TestAutomation(EditorTestSuite): class AtomEditorComponents_DepthOfFieldAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_DepthOfFieldAdded as test_module + @pytest.mark.test_case_id("C36525659") + class AtomEditorComponents_DiffuseProbeGridAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_DiffuseProbeGridAdded as test_module + @pytest.mark.test_case_id("C32078120") class AtomEditorComponents_DirectionalLightAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_DirectionalLightAdded as test_module @@ -37,6 +41,10 @@ class TestAutomation(EditorTestSuite): class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module + @pytest.mark.test_case_id("C36525661") + class AtomEditorComponents_EntityReferenceAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_EntityReferenceAdded as test_module + @pytest.mark.test_case_id("C32078121") class AtomEditorComponents_ExposureControlAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_ExposureControlAdded as test_module @@ -61,6 +69,10 @@ class TestAutomation(EditorTestSuite): class AtomEditorComponents_LightAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_LightAdded as test_module + @pytest.mark.test_case_id("C36525662") + class AtomEditorComponents_LookModificationAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_LookModificationAdded as test_module + @pytest.mark.test_case_id("C32078123") class AtomEditorComponents_MaterialAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_MaterialAdded as test_module @@ -98,5 +110,9 @@ class TestAutomation(EditorTestSuite): class AtomEditorComponents_ReflectionProbeAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_ReflectionProbeAdded as test_module + @pytest.mark.test_case_id("C36525666") + class AtomEditorComponents_SSAOAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_SSAOAdded as test_module + class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest): from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py index 0576de0a62..0f96bc5424 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_HDRiSkyboxAdded.py @@ -6,9 +6,6 @@ SPDX-License-Identifier: Apache-2.0 OR MIT """ -#fmt: off - - class Tests: creation_undo = ( "UNDO Entity creation success", @@ -19,9 +16,12 @@ class Tests: hdri_skybox_entity_creation = ( "HDRi Skybox successfully created", "HDRi Skybox failed to be created") - hdri_skybox_entity_component_added = ( + hdri_skybox_component = ( "Entity has an HDRi Skybox component", "Entity failed to find HDRi Skybox component") + cubemap_property_set = ( + "Cubemap property set on HDRi Skybox component", + "Couldn't set Cubemap property on HDRi Skybox component") enter_game_mode = ( "Entered game mode", "Failed to enter game mode") @@ -45,9 +45,6 @@ class Tests: "REDO deletion failed") -#fmt: on - - def AtomEditorComponents_HDRiSkybox_AddedToEntity(): """ Summary: @@ -77,8 +74,11 @@ def AtomEditorComponents_HDRiSkybox_AddedToEntity(): :return: None """ + import os + import azlmbr.legacy.general as general + from editor_python_test_tools.asset_utils import Asset from editor_python_test_tools.editor_entity_utils import EditorEntity from editor_python_test_tools.utils import Report, Tracer, TestHelper from Atom.atom_utils.atom_constants import AtomComponentProperties @@ -100,7 +100,7 @@ def AtomEditorComponents_HDRiSkybox_AddedToEntity(): hdri_skybox_component = hdri_skybox_entity.add_component( AtomComponentProperties.hdri_skybox()) Report.critical_result( - Tests.hdri_skybox_entity_component_added, + Tests.hdri_skybox_component, hdri_skybox_entity.has_component(AtomComponentProperties.hdri_skybox())) # 3. UNDO the entity creation and component addition. @@ -127,33 +127,44 @@ def AtomEditorComponents_HDRiSkybox_AddedToEntity(): general.idle_wait_frames(1) Report.result(Tests.creation_redo, hdri_skybox_entity.exists()) - # 5. Enter/Exit game mode. + + # 5. Set Cubemap Texture on HDRi Skybox component. + skybox_cubemap_asset_path = os.path.join("LightingPresets", "default_iblskyboxcm.exr.streamingimage") + skybox_cubemap_material_asset = Asset.find_asset_by_path(skybox_cubemap_asset_path, False) + hdri_skybox_component.set_component_property_value( + AtomComponentProperties.hdri_skybox('Cubemap Texture'), skybox_cubemap_material_asset.id) + get_cubemap_property = hdri_skybox_component.get_component_property_value( + AtomComponentProperties.hdri_skybox('Cubemap Texture')) + Report.result(Tests.cubemap_property_set, get_cubemap_property == skybox_cubemap_material_asset.id) + + + # 6. Enter/Exit game mode. TestHelper.enter_game_mode(Tests.enter_game_mode) general.idle_wait_frames(1) TestHelper.exit_game_mode(Tests.exit_game_mode) - # 6. Test IsHidden. + # 7. Test IsHidden. hdri_skybox_entity.set_visibility_state(False) Report.result(Tests.is_hidden, hdri_skybox_entity.is_hidden() is True) - # 7. Test IsVisible. + # 8. Test IsVisible. hdri_skybox_entity.set_visibility_state(True) general.idle_wait_frames(1) Report.result(Tests.is_visible, hdri_skybox_entity.is_visible() is True) - # 8. Delete hdri_skybox entity. + # 9. Delete hdri_skybox entity. hdri_skybox_entity.delete() Report.result(Tests.entity_deleted, not hdri_skybox_entity.exists()) - # 9. UNDO deletion. + # 10. UNDO deletion. general.undo() Report.result(Tests.deletion_undo, hdri_skybox_entity.exists()) - # 10. REDO deletion. + # 11. REDO deletion. general.redo() Report.result(Tests.deletion_redo, not hdri_skybox_entity.exists()) - # 11. Look for errors or asserts. + # 12. Look for errors or asserts. TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) for error_info in error_tracer.errors: Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") From f245c0a7c288b0cac0d3bd91cba1863223e17104 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Tue, 16 Nov 2021 20:40:58 -0600 Subject: [PATCH 5/9] Cherry-picking changes for EngineFinder.cmake. (#5682) This fixes the cmake configuration errors when using a project-centric workflow with newly crated projects Original Commit hash: 289d783 fixes #5643 Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- Templates/DefaultProject/Template/cmake/EngineFinder.cmake | 4 ++-- Templates/MinimalProject/Template/cmake/EngineFinder.cmake | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Templates/DefaultProject/Template/cmake/EngineFinder.cmake b/Templates/DefaultProject/Template/cmake/EngineFinder.cmake index 98ad61bae8..15b96eb8a9 100644 --- a/Templates/DefaultProject/Template/cmake/EngineFinder.cmake +++ b/Templates/DefaultProject/Template/cmake/EngineFinder.cmake @@ -13,8 +13,8 @@ include_guard() # Read the engine name from the project_json file -file(READ ${CMAKE_CURRENT_LIST_DIR}/project.json project_json) -set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/project.json) +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/project.json project_json) +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/project.json) string(JSON LY_ENGINE_NAME_TO_USE ERROR_VARIABLE json_error GET ${project_json} engine) if(json_error) diff --git a/Templates/MinimalProject/Template/cmake/EngineFinder.cmake b/Templates/MinimalProject/Template/cmake/EngineFinder.cmake index 98ad61bae8..15b96eb8a9 100644 --- a/Templates/MinimalProject/Template/cmake/EngineFinder.cmake +++ b/Templates/MinimalProject/Template/cmake/EngineFinder.cmake @@ -13,8 +13,8 @@ include_guard() # Read the engine name from the project_json file -file(READ ${CMAKE_CURRENT_LIST_DIR}/project.json project_json) -set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/project.json) +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/project.json project_json) +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/project.json) string(JSON LY_ENGINE_NAME_TO_USE ERROR_VARIABLE json_error GET ${project_json} engine) if(json_error) From e07cb1f2ed23725e714d6938f3ea27de4b7fa50e Mon Sep 17 00:00:00 2001 From: AMZN-AlexOteiza <82234181+AMZN-AlexOteiza@users.noreply.github.com> Date: Wed, 17 Nov 2021 14:30:41 +0000 Subject: [PATCH 6/9] Re-enabled linux tests physics (#5701) * Re-enabled linux tests physics * Fix for python load errors on Linux (#5627) * Explicitly load libpython on Linux Downstream loads of python modules that weren't linked to libpython would fail to load because libraries were loaded using the RTLD_LOCAL flag. This adds a function that will explicitly load libpython on Linux using the RTLD_GLOBAL flag. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Fix misspelled function name Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Addressing PR feedback - Updates naming and location of things. - Adds load code to a Gem template. - Updates error checking. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Address further feedback Removes the api function in favor of just having modules inherit off a PythonLoader class, that way we get RAAI behavior and lifetime management for free. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Signed-off-by: aljanru Co-authored-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- .../AzToolsFramework/API/PythonLoader.h | 25 ++++++++++++++ .../aztoolsframework_files.cmake | 1 + .../API/PythonLoader_Default.cpp | 20 +++++++++++ .../API/PythonLoader_Linux.cpp | 34 +++++++++++++++++++ .../Platform/Linux/platform_linux_files.cmake | 1 + .../Platform/Mac/platform_mac_files.cmake | 1 + .../Windows/platform_windows_files.cmake | 1 + .../Code/Source/AtomToolsFrameworkModule.h | 2 ++ .../Source/EditorPythonBindingsModule.cpp | 3 ++ .../Code/Source/PythonAssetBuilderModule.cpp | 3 ++ .../Code/Source/${Name}EditorModule.cpp | 2 ++ .../build/Platform/Linux/build_config.json | 4 +-- 12 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/API/PythonLoader.h create mode 100644 Code/Framework/AzToolsFramework/Platform/Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp create mode 100644 Code/Framework/AzToolsFramework/Platform/Linux/AzToolsFramework/API/PythonLoader_Linux.cpp diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/PythonLoader.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/PythonLoader.h new file mode 100644 index 0000000000..29125667d6 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/PythonLoader.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +namespace AzToolsFramework::EmbeddedPython +{ + // When using embedded Python, some platforms need to explicitly load the python library. + // For any modules that depend on 3rdParty::Python package, the AZ::Module should inherit this class. + class PythonLoader + { + public: + PythonLoader(); + ~PythonLoader(); + + private: + void* m_embeddedLibPythonHandle{ nullptr }; + }; + +} // namespace AzToolsFramework::EmbeddedPython diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 286ef97418..65ef8e34af 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -47,6 +47,7 @@ set(FILES API/EntityCompositionRequestBus.h API/EntityCompositionNotificationBus.h API/EditorViewportIconDisplayInterface.h + API/PythonLoader.h API/ViewPaneOptions.h API/ViewportEditorModeTrackerInterface.h Application/Ticker.h diff --git a/Code/Framework/AzToolsFramework/Platform/Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp b/Code/Framework/AzToolsFramework/Platform/Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp new file mode 100644 index 0000000000..42fef21db6 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Platform/Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp @@ -0,0 +1,20 @@ +/* +* 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 + +namespace AzToolsFramework::EmbeddedPython +{ + PythonLoader::PythonLoader() + { + } + + PythonLoader::~PythonLoader() + { + } +} diff --git a/Code/Framework/AzToolsFramework/Platform/Linux/AzToolsFramework/API/PythonLoader_Linux.cpp b/Code/Framework/AzToolsFramework/Platform/Linux/AzToolsFramework/API/PythonLoader_Linux.cpp new file mode 100644 index 0000000000..76fa36a048 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Platform/Linux/AzToolsFramework/API/PythonLoader_Linux.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +namespace AzToolsFramework::EmbeddedPython +{ + PythonLoader::PythonLoader() + { + constexpr char libPythonName[] = "libpython3.7m.so.1.0"; + if (m_embeddedLibPythonHandle = dlopen(libPythonName, RTLD_NOW | RTLD_GLOBAL); + m_embeddedLibPythonHandle == nullptr) + { + char* err = dlerror(); + AZ_Error("PythonLoader", false, "Failed to load %s with error: %s\n", libPythonName, err ? err : "Unknown Error"); + } + } + + PythonLoader::~PythonLoader() + { + if (m_embeddedLibPythonHandle) + { + dlclose(m_embeddedLibPythonHandle); + } + } + +} // namespace AzToolsFramework::EmbeddedPython diff --git a/Code/Framework/AzToolsFramework/Platform/Linux/platform_linux_files.cmake b/Code/Framework/AzToolsFramework/Platform/Linux/platform_linux_files.cmake index c2c5a11c4c..3b04a903a4 100644 --- a/Code/Framework/AzToolsFramework/Platform/Linux/platform_linux_files.cmake +++ b/Code/Framework/AzToolsFramework/Platform/Linux/platform_linux_files.cmake @@ -7,4 +7,5 @@ # set(FILES + AzToolsFramework/API/PythonLoader_Linux.cpp ) diff --git a/Code/Framework/AzToolsFramework/Platform/Mac/platform_mac_files.cmake b/Code/Framework/AzToolsFramework/Platform/Mac/platform_mac_files.cmake index c2c5a11c4c..6342747a38 100644 --- a/Code/Framework/AzToolsFramework/Platform/Mac/platform_mac_files.cmake +++ b/Code/Framework/AzToolsFramework/Platform/Mac/platform_mac_files.cmake @@ -7,4 +7,5 @@ # set(FILES + ../Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp ) diff --git a/Code/Framework/AzToolsFramework/Platform/Windows/platform_windows_files.cmake b/Code/Framework/AzToolsFramework/Platform/Windows/platform_windows_files.cmake index c2c5a11c4c..6342747a38 100644 --- a/Code/Framework/AzToolsFramework/Platform/Windows/platform_windows_files.cmake +++ b/Code/Framework/AzToolsFramework/Platform/Windows/platform_windows_files.cmake @@ -7,4 +7,5 @@ # set(FILES + ../Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp ) diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.h index 759b2558bf..ae60220314 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.h @@ -9,11 +9,13 @@ #pragma once #include +#include namespace AtomToolsFramework { class AtomToolsFrameworkModule : public AZ::Module + , public AzToolsFramework::EmbeddedPython::PythonLoader { public: AZ_RTTI(AtomToolsFrameworkModule, "{B58B7CA8-98C9-4DC8-8607-E094989BBBE2}", AZ::Module); diff --git a/Gems/EditorPythonBindings/Code/Source/EditorPythonBindingsModule.cpp b/Gems/EditorPythonBindings/Code/Source/EditorPythonBindingsModule.cpp index cef8931722..f168cc5ab2 100644 --- a/Gems/EditorPythonBindings/Code/Source/EditorPythonBindingsModule.cpp +++ b/Gems/EditorPythonBindings/Code/Source/EditorPythonBindingsModule.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include #include #include @@ -18,6 +20,7 @@ namespace EditorPythonBindings { class EditorPythonBindingsModule : public AZ::Module + , public AzToolsFramework::EmbeddedPython::PythonLoader { public: AZ_RTTI(EditorPythonBindingsModule, "{851B9E35-4FD5-49B1-8207-E40D4BBA36CC}", AZ::Module); diff --git a/Gems/PythonAssetBuilder/Code/Source/PythonAssetBuilderModule.cpp b/Gems/PythonAssetBuilder/Code/Source/PythonAssetBuilderModule.cpp index d7143a159a..1d330c90df 100644 --- a/Gems/PythonAssetBuilder/Code/Source/PythonAssetBuilderModule.cpp +++ b/Gems/PythonAssetBuilder/Code/Source/PythonAssetBuilderModule.cpp @@ -9,12 +9,15 @@ #include #include +#include + #include namespace PythonAssetBuilder { class PythonAssetBuilderModule : public AZ::Module + , public AzToolsFramework::EmbeddedPython::PythonLoader { public: AZ_RTTI(PythonAssetBuilderModule, "{35C9457E-54C2-474C-AEBE-5A70CC1D435D}", AZ::Module); diff --git a/Templates/PythonToolGem/Template/Code/Source/${Name}EditorModule.cpp b/Templates/PythonToolGem/Template/Code/Source/${Name}EditorModule.cpp index 0027af011a..fdd971440e 100644 --- a/Templates/PythonToolGem/Template/Code/Source/${Name}EditorModule.cpp +++ b/Templates/PythonToolGem/Template/Code/Source/${Name}EditorModule.cpp @@ -10,6 +10,7 @@ #include <${Name}ModuleInterface.h> #include <${Name}EditorSystemComponent.h> +#include void Init${SanitizedCppName}Resources() { @@ -21,6 +22,7 @@ namespace ${SanitizedCppName} { class ${SanitizedCppName}EditorModule : public ${SanitizedCppName}ModuleInterface + , public AzToolsFramework::EmbeddedPython::PythonLoader { public: AZ_RTTI(${SanitizedCppName}EditorModule, "${ModuleClassId}", ${SanitizedCppName}ModuleInterface); diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index 3ae34bf16a..f485f43315 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -83,7 +83,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E (AutomatedTesting::Atom_TestSuite_Main|AutomatedTesting::PhysicsTests_Main|AutomatedTesting::PrefabTests|AutomatedTesting::TerrainTests_Main|Gem::EMotionFX.Editor.Tests) -L (SUITE_smoke|SUITE_main) -LE (REQUIRES_gpu) --no-tests=error", + "CTEST_OPTIONS": "-E (AutomatedTesting::Atom_TestSuite_Main|AutomatedTesting::PrefabTests|AutomatedTesting::TerrainTests_Main|Gem::EMotionFX.Editor.Tests) -L (SUITE_smoke|SUITE_main) -LE (REQUIRES_gpu) --no-tests=error", "TEST_RESULTS": "True" } }, @@ -96,7 +96,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E (AutomatedTesting::Atom_TestSuite_Main|AutomatedTesting::PhysicsTests_Main|AutomatedTesting::PrefabTests|AutomatedTesting::TerrainTests_Main|Gem::EMotionFX.Editor.Tests) -L (SUITE_smoke|SUITE_main) -LE (REQUIRES_gpu) --no-tests=error", + "CTEST_OPTIONS": "-E (AutomatedTesting::Atom_TestSuite_Main|AutomatedTesting::PrefabTests|AutomatedTesting::TerrainTests_Main|Gem::EMotionFX.Editor.Tests) -L (SUITE_smoke|SUITE_main) -LE (REQUIRES_gpu) --no-tests=error", "TEST_RESULTS": "True" } }, From 13782187a3de51887c07c4016bb6a30924d3aca2 Mon Sep 17 00:00:00 2001 From: Nicholas Lawson <70027408+lawsonamzn@users.noreply.github.com> Date: Wed, 17 Nov 2021 08:03:42 -0800 Subject: [PATCH 7/9] Update astc-encoder package to use SSE4.1 across all platforms (#5676) * Updates o3de to point at the new sse4.1-enabled astc encoder package Signed-off-by: lawsonamzn <70027408+lawsonamzn@users.noreply.github.com> --- cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake | 2 +- cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake | 2 +- cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake index 25424a348c..995e6082f3 100644 --- a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake +++ b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake @@ -43,6 +43,6 @@ ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-linux ly_associate_package(PACKAGE_NAME azslc-1.7.34-rev1-linux TARGETS azslc PACKAGE_HASH 6d7dc671936c34ff70d2632196107ca1b8b2b41acdd021bfbc69a9fd56215c22) ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev5-linux TARGETS ZLIB PACKAGE_HASH 9be5ea85722fc27a8645a9c8a812669d107c68e6baa2ca0740872eaeb6a8b0fc) ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-linux TARGETS squish-ccr PACKAGE_HASH 85fecafbddc6a41a27c5f59ed4a5dfb123a94cb4666782cf26e63c0a4724c530) -ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-linux TARGETS astc-encoder PACKAGE_HASH 2ba97a06474d609945f0ab4419af1f6bbffdd294ca6b869f5fcebec75c573c0f) +ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev2-linux TARGETS astc-encoder PACKAGE_HASH 71549d1ca9e4d48391b92a89ea23656d3393810e6777879f6f8a9def2db1610c) ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-linux TARGETS ISPCTexComp PACKAGE_HASH 065fd12abe4247dde247330313763cf816c3375c221da030bdec35024947f259) ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-linux TARGETS lz4 PACKAGE_HASH 5de3dbd3e2a3537c6555d759b3c5bb98e5456cf85c74ff6d046f809b7087290d) diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index 1ac7568a98..eef6badf45 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -40,7 +40,7 @@ ly_associate_package(PACKAGE_NAME libpng-1.6.37-mac ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-mac TARGETS libsamplerate PACKAGE_HASH b912af40c0ac197af9c43d85004395ba92a6a859a24b7eacd920fed5854a97fe) ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev5-mac TARGETS ZLIB PACKAGE_HASH b6fea9c79b8bf106d4703b67fecaa133f832ad28696c2ceef45fb5f20013c096) ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-mac TARGETS squish-ccr PACKAGE_HASH 155bfbfa17c19a9cd2ef025de14c5db598f4290045d5b0d83ab58cb345089a77) -ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-mac TARGETS astc-encoder PACKAGE_HASH 96f6ea8c3e45ec7fe525230c7c53ca665c8300d8e28456cc19bb3159ce6f8dcc) +ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev2-mac TARGETS astc-encoder PACKAGE_HASH 06f129d26995845824f1fb906a5135b2c71d44d66c768342af85fa28a175906f) ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-mac TARGETS ISPCTexComp PACKAGE_HASH 8a4e93277b8face6ea2fd57c6d017bdb55643ed3d6387110bc5f6b3b884dd169) ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-mac TARGETS lz4 PACKAGE_HASH 891ff630bf34f7ab1d8eaee2ea0a8f1fca89dbdc63fca41ee592703dd488a73b) ly_associate_package(PACKAGE_NAME azslc-1.7.34-rev1-mac TARGETS azslc PACKAGE_HASH a9d81946b42ffa55c0d14d6a9249b3340e59a8fb8835e7a96c31df80f14723bc) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 3189625611..3038e8c561 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -46,7 +46,7 @@ ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-windows ly_associate_package(PACKAGE_NAME Crashpad-0.8.0-rev1-windows TARGETS Crashpad PACKAGE_HASH d162aa3070147bc0130a44caab02c5fe58606910252caf7f90472bd48d4e31e2) ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev5-windows TARGETS ZLIB PACKAGE_HASH 8847112429744eb11d92c44026fc5fc53caa4a06709382b5f13978f3c26c4cbd) ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-windows TARGETS squish-ccr PACKAGE_HASH 5c3d9fa491e488ccaf802304ad23b932268a2b2846e383f088779962af2bfa84) -ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-windows TARGETS astc-encoder PACKAGE_HASH 3addc6fc1a7eb0d6b7f3d530e962af967e6d92b3825ef485da243346357cf78e) +ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev2-windows TARGETS astc-encoder PACKAGE_HASH 17249bfa438afb34e21449865d9c9297471174ae0cea9b2f9def2ee206038295) ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-windows TARGETS ISPCTexComp PACKAGE_HASH b6fa6ea28a2808a9a5524c72c37789c525925e435770f2d94eb2d387360fa2d0) ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-windows TARGETS lz4 PACKAGE_HASH 4ea457b833cd8cfaf8e8e06ed6df601d3e6783b606bdbc44a677f77e19e0db16) ly_associate_package(PACKAGE_NAME azslc-1.7.34-rev1-windows TARGETS azslc PACKAGE_HASH 44eb2e0fc4b0f1c75d0fb6f24c93a5753655b84dbc3e6ad45389ed3b9cf7a4b0) From 9b6e2ed51dd9dab5163907c77a83f8958d5a514b Mon Sep 17 00:00:00 2001 From: Roman <69218254+amzn-rhhong@users.noreply.github.com> Date: Wed, 17 Nov 2021 08:05:03 -0800 Subject: [PATCH 8/9] render solid skeleton option (#5610) * render solid skeleton option Signed-off-by: rhhong * Fix broken build Signed-off-by: rhhong * CR update Signed-off-by: rhhong --- .../Code/Source/AtomActorDebugDraw.cpp | 69 ++++++++++++++++++- .../Code/Source/AtomActorDebugDraw.h | 3 + .../Source/RenderPlugin/RenderOptions.cpp | 2 + 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp index 89053f6349..698dd4cd75 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp @@ -49,8 +49,14 @@ namespace AZ::Render RenderAABB(instance, renderActorSettings.m_staticAABBColor); } - // Render skeleton + // Render simple line skeleton if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_LINESKELETON]) + { + RenderLineSkeleton(instance, renderActorSettings.m_lineSkeletonColor); + } + + // Render advance skeleton + if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_SKELETON]) { RenderSkeleton(instance, renderActorSettings.m_skeletonColor); } @@ -110,6 +116,29 @@ namespace AZ::Render return aabbRadius * 0.01f; } + float AtomActorDebugDraw::CalculateBoneScale(EMotionFX::ActorInstance* actorInstance, EMotionFX::Node* node) + { + // Get the transform data + EMotionFX::TransformData* transformData = actorInstance->GetTransformData(); + const EMotionFX::Pose* pose = transformData->GetCurrentPose(); + + const size_t nodeIndex = node->GetNodeIndex(); + const size_t parentIndex = node->GetParentIndex(); + const AZ::Vector3 nodeWorldPos = pose->GetWorldSpaceTransform(nodeIndex).m_position; + + if (parentIndex != InvalidIndex) + { + const AZ::Vector3 parentWorldPos = pose->GetWorldSpaceTransform(parentIndex).m_position; + const AZ::Vector3 bone = parentWorldPos - nodeWorldPos; + const float boneLength = bone.GetLengthEstimate(); + + // 10% of the bone length is the sphere size + return boneLength * 0.1f; + } + + return 0.0f; + } + void AtomActorDebugDraw::PrepareForMesh(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM) { // Check if we have already prepared for the given mesh @@ -145,7 +174,7 @@ namespace AZ::Render auxGeom->DrawAabb(aabb, aabbColor, RPI::AuxGeomDraw::DrawStyle::Line); } - void AtomActorDebugDraw::RenderSkeleton(EMotionFX::ActorInstance* instance, const AZ::Color& skeletonColor) + void AtomActorDebugDraw::RenderLineSkeleton(EMotionFX::ActorInstance* instance, const AZ::Color& skeletonColor) { RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); @@ -189,6 +218,42 @@ namespace AZ::Render auxGeom->DrawLines(lineArgs); } + void AtomActorDebugDraw::RenderSkeleton(EMotionFX::ActorInstance* instance, const AZ::Color& skeletonColor) + { + RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); + + const EMotionFX::TransformData* transformData = instance->GetTransformData(); + const EMotionFX::Skeleton* skeleton = instance->GetActor()->GetSkeleton(); + const EMotionFX::Pose* pose = transformData->GetCurrentPose(); + const size_t numEnabled = instance->GetNumEnabledNodes(); + for (size_t i = 0; i < numEnabled; ++i) + { + EMotionFX::Node* joint = skeleton->GetNode(instance->GetEnabledNode(i)); + const size_t jointIndex = joint->GetNodeIndex(); + const size_t parentIndex = joint->GetParentIndex(); + + // check if this node has a parent and is a bone, if not skip it + if (parentIndex == InvalidIndex) + { + continue; + } + + const AZ::Vector3 nodeWorldPos = pose->GetWorldSpaceTransform(jointIndex).m_position; + const AZ::Vector3 parentWorldPos = pose->GetWorldSpaceTransform(parentIndex).m_position; + const AZ::Vector3 bone = parentWorldPos - nodeWorldPos; + const AZ::Vector3 boneDirection = bone.GetNormalizedEstimate(); + const AZ::Vector3 centerWorldPos = bone / 2 + nodeWorldPos; + const float boneLength = bone.GetLengthEstimate(); + const float boneScale = CalculateBoneScale(instance, joint); + const float parentBoneScale = CalculateBoneScale(instance, skeleton->GetNode(parentIndex)); + const float cylinderSize = boneLength - boneScale - parentBoneScale; + + // Render the bone cylinder, the cylinder will be directed towards the node's parent and must fit between the spheres + auxGeom->DrawCylinder(centerWorldPos, boneDirection, boneScale, cylinderSize, skeletonColor); + auxGeom->DrawSphere(nodeWorldPos, boneScale, skeletonColor); + } + } + void AtomActorDebugDraw::RenderEMFXDebugDraw(EMotionFX::ActorInstance* instance) { RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h index 4178ad25f1..60db07cd8e 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h @@ -37,9 +37,12 @@ namespace AZ::Render private: + float CalculateBoneScale(EMotionFX::ActorInstance* actorInstance, EMotionFX::Node* node); float CalculateScaleMultiplier(EMotionFX::ActorInstance* instance) const; void PrepareForMesh(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM); + void RenderAABB(EMotionFX::ActorInstance* instance, const AZ::Color& aabbColor); + void RenderLineSkeleton(EMotionFX::ActorInstance* instance, const AZ::Color& skeletonColor); void RenderSkeleton(EMotionFX::ActorInstance* instance, const AZ::Color& skeletonColor); void RenderEMFXDebugDraw(EMotionFX::ActorInstance* instance); void RenderNormals( diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp index d1e3dc5ebc..3d1b0e3150 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp @@ -545,6 +545,7 @@ namespace EMStudio ->Attribute(AZ::Edit::Attributes::ChangeNotify, &RenderOptions::OnLineSkeletonColorChangedCallback) ->DataElement(AZ::Edit::UIHandlers::Default, &RenderOptions::m_skeletonColor, "Solid skeleton color", "Solid skeleton color.") + ->Attribute(AZ_CRC("AlphaChannel", 0xa0cab5cf), true) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &RenderOptions::OnSkeletonColorChangedCallback) ->DataElement(AZ::Edit::UIHandlers::Default, &RenderOptions::m_selectionColor, "Selection gizmo color", "Selection gizmo color") @@ -1268,6 +1269,7 @@ namespace EMStudio void RenderOptions::OnSkeletonColorChangedCallback() const { PluginOptionsNotificationsBus::Event(s_skeletonColorOptionName, &PluginOptionsNotificationsBus::Events::OnOptionChanged, s_skeletonColorOptionName); + CopyToRenderActorSettings(EMotionFX::GetRenderActorSettings()); } void RenderOptions::OnSelectionColorChangedCallback() const From 8bccd36d0390d6b38514d65ad02806c4b7db334b Mon Sep 17 00:00:00 2001 From: Ken Pruiksma Date: Wed, 17 Nov 2021 10:30:06 -0600 Subject: [PATCH 9/9] Terrain detail textures support with bindless arrays (#5460) * Added buffer for material properties of detail mateirals, storing them in a multi-indexed data vector. Updated shader with relevant struct and buffer, but the buffer will need to be moved out of the mateiral SRG to work. Signed-off-by: Ken Pruiksma * Added buffer for material properties of detail mateirals, storing them in a multi-indexed data vector. Updated shader with relevant struct and buffer, but the buffer will need to be moved out of the mateiral SRG to work. Signed-off-by: Ken Pruiksma * Added buffer for material properties of detail mateirals, storing them in a multi-indexed data vector. Updated shader with relevant struct and buffer, but the buffer will need to be moved out of the mateiral SRG to work. Signed-off-by: Ken Pruiksma * - Moved settings related to the detail material to a partial view srg owned by the terrain gem. - Added support for base color in detail materials. - Hooked up basic base color rendering of detail materials. - Corrected the way the material data was stored. - Added ref counting for detail materials so they can be released when no longer used. Signed-off-by: Ken Pruiksma * Added buffer for material properties of detail mateirals, storing them in a multi-indexed data vector. Updated shader with relevant struct and buffer, but the buffer will need to be moved out of the mateiral SRG to work. Signed-off-by: Ken Pruiksma * - Moved settings related to the detail material to a partial view srg owned by the terrain gem. - Added support for base color in detail materials. - Hooked up basic base color rendering of detail materials. - Corrected the way the material data was stored. - Added ref counting for detail materials so they can be released when no longer used. Signed-off-by: Ken Pruiksma * Detail materials now put textures into bindless array that's accessed in the shader. Shader now pulls all the detail materal information for a single mateiral but does no blending. Signed-off-by: Ken Pruiksma * Correcting rebase merge problem. Signed-off-by: Ken Pruiksma * Fix detail roughness fade out with distance. Signed-off-by: Ken Pruiksma * Adding tests for new MultiIndexedDataVector functions Signed-off-by: Ken Pruiksma * Updates to move bindless array to separate SRG - Exposed BindSrg() in renderpass so it's possible to add additional SRGs to a pass - Created a TerrainSrg for use by the terrain forward shader - Moved the bindless array out of the partial view SRG to the TerrainSrg Signed-off-by: Ken Pruiksma * Moved more properties out of the view srg to the terrain srg. Signed-off-by: Ken Pruiksma * Spelling fixes Signed-off-by: Ken Pruiksma * Fixing bug where the roughness min/max value were inverted. Also fixed bug where bad data would show for areas where there was no macro material. Signed-off-by: Ken Pruiksma * Updates from PR review Signed-off-by: Ken Pruiksma * Fixing case issues and updating function name due to a recent fix. Signed-off-by: Ken Pruiksma --- .../ShaderResourceGroups/ViewSrgAll.azsli | 1 + .../Feature/Utils/MultiIndexedDataVector.h | 22 + .../Code/Tests/IndexedDataVectorTests.cpp | 222 +++++++- .../Include/Atom/RPI.Public/Pass/RenderPass.h | 7 +- .../Materials/Terrain/PbrTerrain.materialtype | 261 --------- .../Shaders/Terrain/TerrainCommon.azsli | 42 +- .../Terrain/TerrainDetailHelpers.azsli | 250 +++++++++ .../Terrain/TerrainPBR_ForwardPass.azsl | 147 +++-- .../Assets/Shaders/Terrain/TerrainSrg.azsli | 74 +++ .../Assets/Shaders/Terrain/ViewSrg.azsli | 80 +++ .../TerrainFeatureProcessor.cpp | 520 ++++++++++++------ .../TerrainRenderer/TerrainFeatureProcessor.h | 85 ++- 12 files changed, 1140 insertions(+), 571 deletions(-) create mode 100644 Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli create mode 100644 Gems/Terrain/Assets/Shaders/Terrain/TerrainSrg.azsli create mode 100644 Gems/Terrain/Assets/Shaders/Terrain/ViewSrg.azsli diff --git a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/ViewSrgAll.azsli b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/ViewSrgAll.azsli index 3e90e1a441..9253885cfc 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/ViewSrgAll.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/ViewSrgAll.azsli @@ -12,4 +12,5 @@ #ifdef AZ_COLLECTING_PARTIAL_SRGS #include +#include // Temporary until gem partial view srgs can be included automatically. #endif diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/MultiIndexedDataVector.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/MultiIndexedDataVector.h index a1a6e329b2..e815d85cf3 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/MultiIndexedDataVector.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/MultiIndexedDataVector.h @@ -199,6 +199,28 @@ namespace AZ { return m_indices.at(index); } + + template + IndexType GetIndexForData(const DataType* data) const + { + if (data >= &AZStd::get(m_data).front() && data <= &AZStd::get(m_data).back()) + { + return m_dataToIndices.at(data - &AZStd::get(m_data).front()); + } + return NoFreeSlot; + } + + template + void ForEach(LambdaType lambda) const + { + for (auto& item : AZStd::get(m_data)) + { + if (!lambda(item)) + { + break; + } + } + } private: using Fn = void(&)(AZStd::vector& ...); diff --git a/Gems/Atom/Feature/Common/Code/Tests/IndexedDataVectorTests.cpp b/Gems/Atom/Feature/Common/Code/Tests/IndexedDataVectorTests.cpp index 6a13b9b774..a9cb6e2621 100644 --- a/Gems/Atom/Feature/Common/Code/Tests/IndexedDataVectorTests.cpp +++ b/Gems/Atom/Feature/Common/Code/Tests/IndexedDataVectorTests.cpp @@ -31,7 +31,7 @@ namespace UnitTest { DestroyAllocator(); } - + private: void CreateAllocator() @@ -60,6 +60,12 @@ namespace UnitTest TEST_F(IndexedDataVectorTests, TestInsert) { + enum Types + { + IntType = 0, + DoubleType = 1, + }; + MultiIndexedDataVector myVec; constexpr int NumToInsert = 5; @@ -69,38 +75,48 @@ namespace UnitTest { auto index = myVec.GetFreeSlotIndex(); indices.push_back(index); - myVec.GetData<0>(index) = i; - myVec.GetData<1>(index) = (double)i; + myVec.GetData(index) = i; + myVec.GetData(index) = (double)i; } for (size_t i = 0; i < NumToInsert; ++i) { auto index = indices[i]; - EXPECT_EQ(i, myVec.GetData<0>(index)); - EXPECT_EQ((double)i, myVec.GetData<1>(index)); + EXPECT_EQ(i, myVec.GetData(index)); + EXPECT_EQ((double)i, myVec.GetData(index)); } } TEST_F(IndexedDataVectorTests, TestSize) { + enum Types + { + IntType = 0, + }; + MultiIndexedDataVector myVec; constexpr int NumToInsert = 5; for (int i = 0; i < NumToInsert; ++i) { auto index = myVec.GetFreeSlotIndex(); - myVec.GetData<0>(index) = i; + myVec.GetData(index) = i; } EXPECT_EQ(NumToInsert, myVec.GetDataCount()); - EXPECT_EQ(NumToInsert, myVec.GetDataVector<0>().size()); + EXPECT_EQ(NumToInsert, myVec.GetDataVector().size()); myVec.Clear(); EXPECT_EQ(0, myVec.GetDataCount()); - EXPECT_EQ(0, myVec.GetDataVector<0>().size()); + EXPECT_EQ(0, myVec.GetDataVector().size()); } TEST_F(IndexedDataVectorTests, TestErase) { + enum Types + { + IntType = 0, + }; + MultiIndexedDataVector myVec; constexpr int NumToInsert = 200; AZStd::unordered_map valueToIndex; @@ -109,7 +125,7 @@ namespace UnitTest { auto index = myVec.GetFreeSlotIndex(); valueToIndex[i] = index; - myVec.GetData<0>(index) = i; + myVec.GetData(index) = i; } // erase every even number @@ -133,12 +149,21 @@ namespace UnitTest { int val = iter.first; uint16_t index = iter.second; - EXPECT_EQ(val, myVec.GetData<0>(index)); + EXPECT_EQ(val, myVec.GetData(index)); } } TEST_F(IndexedDataVectorTests, TestManyTypes) { + enum Types + { + IntType = 0, + StringType = 1, + DoubleType = 2, + FloatType = 3, + CharType = 4, + }; + MultiIndexedDataVector myVec; auto index = myVec.GetFreeSlotIndex(); @@ -148,16 +173,173 @@ namespace UnitTest constexpr float TestFloatVal = FLT_MAX; const char* TestConstPointerVal = "This is a C array."; - myVec.GetData<0>(index) = TestIntVal; - myVec.GetData<1>(index) = TestStringVal; - myVec.GetData<2>(index) = TestDoubleVal; - myVec.GetData<3>(index) = TestFloatVal; - myVec.GetData<4>(index) = TestConstPointerVal; + myVec.GetData(index) = TestIntVal; + myVec.GetData(index) = TestStringVal; + myVec.GetData(index) = TestDoubleVal; + myVec.GetData(index) = TestFloatVal; + myVec.GetData(index) = TestConstPointerVal; + + EXPECT_EQ(TestIntVal, static_cast(myVec.GetData(index))); + EXPECT_EQ(TestStringVal, static_cast(myVec.GetData(index))); + EXPECT_EQ(TestDoubleVal, static_cast(myVec.GetData(index))); + EXPECT_EQ(TestFloatVal, static_cast(myVec.GetData(index))); + EXPECT_STREQ(TestConstPointerVal, static_cast(myVec.GetData(index))); + } + + MultiIndexedDataVector CreateTestVector(AZStd::vector& indices) + { + enum Types + { + IntType = 0, + FloatType = 1, + }; + + MultiIndexedDataVector myVec; + constexpr int32_t Count = 10; + int32_t startInt = 10; + float startFloat = 2.0f; + + // Create some initial values + for (uint32_t i = 0; i < Count; ++i) + { + uint16_t index = myVec.GetFreeSlotIndex(); + indices.push_back(index); + myVec.GetData(index) = startInt; + myVec.GetData(index) = startFloat; + startInt += 1; + startFloat += 1.0f; + } + + return myVec; + } + + void CheckIndexedData(MultiIndexedDataVector& data, AZStd::vector& indices) + { + enum Types + { + IntType = 0, + FloatType = 1, + }; + + // For each index, get its data and make sure GetIndexForData returns the same + // index used to retrieve the data + for (uint32_t i = 0; i < data.GetDataCount(); ++i) + { + int32_t& intData = data.GetData(indices.at(i)); + uint16_t indexForData = data.GetIndexForData(&intData); + EXPECT_EQ(indices.at(i), indexForData); + + float& floatData = data.GetData(indices.at(i)); + indexForData = data.GetIndexForData(&floatData); + EXPECT_EQ(indices.at(i), indexForData); + } + } + + TEST_F(IndexedDataVectorTests, GetIndexForDataSimple) + { + AZStd::vector indices; + MultiIndexedDataVector myVec = CreateTestVector(indices); + CheckIndexedData(myVec, indices); + } + + TEST_F(IndexedDataVectorTests, GetIndexForDataComplex) + { + enum Types + { + IntType = 0, + FloatType = 1, + }; + + AZStd::vector indices; + MultiIndexedDataVector myVec = CreateTestVector(indices); + + // remove every other value to shuffle the data around + for (uint32_t i = 0; i < myVec.GetDataCount(); i += 2) + { + myVec.RemoveIndex(indices.at(i)); + } + + int32_t startInt = 100; + float startFloat = 20.0f; + + // Add some data back in + const size_t count = myVec.GetDataCount(); + for (uint32_t i = 0; i < count; i += 2) + { + uint16_t index = myVec.GetFreeSlotIndex(); + indices.at(i) = index; + myVec.GetData(index) = startInt; + myVec.GetData(index) = startFloat; + startInt += 1; + startFloat += 1.0f; + } + + CheckIndexedData(myVec, indices); + } + + TEST_F(IndexedDataVectorTests, ForEach) + { + enum Types + { + IntType = 0, + FloatType = 1, + }; + + MultiIndexedDataVector myVec; + constexpr int32_t Count = 10; + int32_t startInt = 10; + float startFloat = 2.0f; + + AZStd::vector indices; + AZStd::set intValues; + AZStd::set floatValues; + + // Create some initial values + for (uint32_t i = 0; i < Count; ++i) + { + uint16_t index = myVec.GetFreeSlotIndex(); + indices.push_back(index); + myVec.GetData(index) = startInt; + myVec.GetData(index) = startFloat; + intValues.insert(startInt); + floatValues.insert(startFloat); + startInt += 1; + startFloat += 1.0f; + } + + uint32_t visitCount = 0; + myVec.ForEach([&](int32_t value) -> bool + { + intValues.erase(value); + ++visitCount; + return true; // keep iterating + }); + + // All ints should have been visited and found in the set + EXPECT_EQ(visitCount, Count); + EXPECT_EQ(intValues.size(), 0); + + visitCount = 0; + myVec.ForEach([&](float value) -> bool + { + floatValues.erase(value); + ++visitCount; + return true; // keep iterating + }); + + // All floats should have been visited and found in the set + EXPECT_EQ(visitCount, Count); + EXPECT_EQ(floatValues.size(), 0); + + visitCount = 0; + myVec.ForEach([&]([[maybe_unused]] int32_t value) -> bool + { + ++visitCount; + return false; // stop iterating + }); + + // Since false is immediately returned, only one element should have been visited. + EXPECT_EQ(visitCount, 1); - EXPECT_EQ(TestIntVal, static_cast(myVec.GetData<0>(index))); - EXPECT_EQ(TestStringVal, static_cast(myVec.GetData<1>(index))); - EXPECT_EQ(TestDoubleVal, static_cast(myVec.GetData<2>(index))); - EXPECT_EQ(TestFloatVal, static_cast(myVec.GetData<3>(index))); - EXPECT_STREQ(TestConstPointerVal, static_cast(myVec.GetData<4>(index))); } } diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h index ec51e34897..bdd305b4eb 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/RenderPass.h @@ -62,6 +62,10 @@ namespace AZ //! It may return nullptr if this pass is independent with any views. ViewPtr GetView() const; + // Add a srg to srg list to be bound for this pass + void BindSrg(const RHI::ShaderResourceGroup* srg); + + protected: explicit RenderPass(const PassDescriptor& descriptor); @@ -95,9 +99,6 @@ namespace AZ // Clear the srg list void ResetSrgs(); - // Add a srg to srg list to be bound for this pass - void BindSrg(const RHI::ShaderResourceGroup* srg); - // Set srgs for pass's execution void SetSrgsForDraw(RHI::CommandList* commandList); void SetSrgsForDispatch(RHI::CommandList* commandList); diff --git a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype index 235079d95e..a20ac23e17 100644 --- a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype +++ b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype @@ -89,61 +89,6 @@ } ], "settings": [ - { - "id": "heightmapImage", - "displayName": "Heightmap Image", - "description": "Heightmap of the terrain. Controlled by the runtime.", - "visibility": "Hidden", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_heightmapImage" - } - }, - { - "id": "detailMaterialIdImage", - "displayName": "Detail Material Id Image", - "description": "Texture containing detail material Ids and weights. Controlled by the runtime.", - "visibility": "Hidden", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_detailMaterialIdImage" - } - }, - { - "id": "detailMaterialIdCenter", - "displayName": "Detail Material Id Image Center", - "description": "The center position of the detail material Id image. Controlled by the runtime.", - "visibility": "Hidden", - "type": "Vector2", - "connection": { - "type": "ShaderInput", - "id": "m_detailMaterialIdImageCenter" - } - }, - { - "id": "detailAabb", - "displayName": "Detail material bounds in 2d", - "description": "The 2d world space bounds of the detail id material. Controlled by the runtime.", - "visibility": "Hidden", - "type": "Vector4", - "connection": { - "type": "ShaderInput", - "id": "m_detailAabb" - } - }, - { - "id": "detailHalfPixelUv", - "displayName": "Detail texture half pixel uv size", - "description": "Uv size of a half pixel in the detail material id texture. Controlled by the runtime.", - "visibility": "Hidden", - "type": "float", - "connection": { - "type": "ShaderInput", - "id": "m_detailHalfPixelUv" - } - }, { "id": "detailTextureMultiplier", "displayName": "Detail Texture UV Multiplier", @@ -177,178 +122,6 @@ "id": "m_detailFadeLength" } } - ], - "baseColor": [ - { - "id": "color", - "displayName": "Color", - "description": "Color is displayed as sRGB but the values are stored as linear color.", - "type": "Color", - "defaultValue": [ 1.0, 1.0, 1.0 ], - "connection": { - "type": "ShaderInput", - "id": "m_baseColor" - } - }, - { - "id": "factor", - "displayName": "Factor", - "description": "Strength factor for scaling the base color values. Zero (0.0) is black, white (1.0) is full color.", - "type": "Float", - "defaultValue": 1.0, - "min": 0.0, - "max": 1.0, - "connection": { - "type": "ShaderInput", - "id": "m_baseColorFactor" - } - }, - { - "id": "textureMap", - "displayName": "Texture", - "description": "Base color texture map", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_baseColorMap" - } - }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture.", - "type": "Bool", - "defaultValue": true - }, - { - "id": "textureBlendMode", - "displayName": "Texture Blend Mode", - "description": "Selects the equation to use when combining Color, Factor, and Texture.", - "type": "Enum", - "enumValues": [ "Multiply", "LinearLight", "Lerp", "Overlay" ], - "defaultValue": "Overlay", - "connection": { - "type": "ShaderOption", - "id": "o_baseColorTextureBlendMode" - } - } - ], - "normal": [ - { - "id": "textureMap", - "displayName": "Texture", - "description": "Texture for defining surface normal direction.", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_normalMap" - } - }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture, or just rely on vertex normals.", - "type": "Bool", - "defaultValue": true - }, - { - "id": "flipX", - "displayName": "Flip X Channel", - "description": "Flip tangent direction for this normal map.", - "type": "Bool", - "defaultValue": false, - "connection": { - "type": "ShaderInput", - "id": "m_flipNormalX" - } - }, - { - "id": "flipY", - "displayName": "Flip Y Channel", - "description": "Flip bitangent direction for this normal map.", - "type": "Bool", - "defaultValue": false, - "connection": { - "type": "ShaderInput", - "id": "m_flipNormalY" - } - }, - { - "id": "factor", - "displayName": "Factor", - "description": "Strength factor for scaling the values", - "type": "Float", - "defaultValue": 1.0, - "min": 0.0, - "softMax": 2.0, - "connection": { - "type": "ShaderInput", - "id": "m_normalFactor" - } - } - ], - "roughness": [ - { - "id": "textureMap", - "displayName": "Texture", - "description": "Texture for defining surface roughness.", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_roughnessMap" - } - }, - { - "id": "useTexture", - "description": "Whether to use the texture, or just default to the Factor value.", - "type": "Bool", - "defaultValue": true - }, - { - "id": "factor", - "displayName": "Factor", - "description": "Controls the roughness value", - "type": "Float", - "defaultValue": 1.0, - "min": 0.0, - "max": 1.0, - "connection": { - "type": "ShaderInput", - "id": "m_roughnessFactor" - } - } - ], - "specularF0": [ - { - "id": "textureMap", - "displayName": "Texture", - "description": "Texture for defining surface reflectance.", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_specularF0Map" - } - }, - { - "id": "useTexture", - "displayName": "Use Texture", - "description": "Whether to use the texture, or just default to the Factor value.", - "type": "Bool", - "defaultValue": true - }, - { - "id": "factor", - "displayName": "Factor", - "description": "The default IOR is 1.5, which gives you 0.04 (4% of light reflected at 0 degree angle for dielectric materials). F0 values lie in the range 0-0.08, so that is why the default F0 slider is set on 0.5.", - "type": "Float", - "defaultValue": 0.5, - "min": 0.0, - "max": 1.0, - "connection": { - "type": "ShaderInput", - "id": "m_specularF0Factor" - } - } ] } }, @@ -364,39 +137,5 @@ } ], "functors": [ - { - "type": "UseTexture", - "args": { - "textureProperty": "baseColor.textureMap", - "useTextureProperty": "baseColor.useTexture", - "dependentProperties": ["baseColor.textureBlendMode"], - "shaderOption": "o_baseColor_useTexture" - } - }, - { - "type": "UseTexture", - "args": { - "textureProperty": "specularF0.textureMap", - "useTextureProperty": "specularF0.useTexture", - "shaderOption": "o_specularF0_useTexture" - } - }, - { - "type": "UseTexture", - "args": { - "textureProperty": "normal.textureMap", - "useTextureProperty": "normal.useTexture", - "dependentProperties": ["normal.factor", "normal.flipX", "normal.flipY"], - "shaderOption": "o_normal_useTexture" - } - }, - { - "type": "UseTexture", - "args": { - "textureProperty": "roughness.textureMap", - "useTextureProperty": "roughness.useTexture", - "shaderOption": "o_roughness_useTexture" - } - } ] } diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli index a06f556638..f5af598435 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli @@ -15,8 +15,6 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject { - row_major float3x4 m_modelToWorld; - struct TerrainData { float2 m_uvMin; @@ -36,6 +34,8 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject uint m_mapsInUse; }; + row_major float3x4 m_modelToWorld; + TerrainData m_terrainData; MacroMaterialData m_macroMaterialData[4]; @@ -43,7 +43,7 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject Texture2D m_macroColorMap[4]; Texture2D m_macroNormalMap[4]; - + // The below shouldn't be in this SRG but needs to be for now because the lighting functions depend on them. //! Reflection Probe (smallest probe volume that overlaps the object position) @@ -93,26 +93,10 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial { - Texture2D m_heightmapImage; - Texture2D m_detailMaterialIdImage; - float2 m_detailMaterialIdImageCenter; float m_detailTextureMultiplier; float m_detailFadeDistance; float m_detailFadeLength; - float4 m_detailAabb; - float m_detailHalfPixelUv; - - Sampler HeightmapSampler - { - MinFilter = Linear; - MagFilter = Linear; - MipFilter = Point; - AddressU = Clamp; - AddressV = Clamp; - AddressW = Clamp; - }; - Sampler m_sampler { AddressU = Wrap; @@ -123,15 +107,6 @@ ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial MaxAnisotropy = 16; }; - Sampler m_detailSampler - { - AddressU = Wrap; - AddressV = Wrap; - MinFilter = Point; - MagFilter = Point; - MipFilter = Point; - }; - // Base Color float3 m_baseColor; float m_baseColorFactor; @@ -153,11 +128,6 @@ ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial } option bool o_useTerrainSmoothing = false; -option bool o_baseColor_useTexture = true; -option bool o_specularF0_useTexture = true; -option bool o_normal_useTexture = true; -option bool o_roughness_useTexture = true; -option TextureBlendMode o_baseColorTextureBlendMode = TextureBlendMode::Multiply; struct VertexInput { @@ -240,12 +210,12 @@ float GetHeight(float2 origUv) if (o_useTerrainSmoothing) { float2 textureSize; - TerrainMaterialSrg::m_heightmapImage.GetDimensions(textureSize.x, textureSize.y); - height = SampleBSpline5Tap(TerrainMaterialSrg::m_heightmapImage, TerrainMaterialSrg::HeightmapSampler, uv, textureSize, rcp(textureSize)); + ViewSrg::m_heightmapImage.GetDimensions(textureSize.x, textureSize.y); + height = SampleBSpline5Tap(ViewSrg::m_heightmapImage, ViewSrg::HeightmapSampler, uv, textureSize, rcp(textureSize)); } else { - height = TerrainMaterialSrg::m_heightmapImage.SampleLevel(TerrainMaterialSrg::HeightmapSampler, uv, 0).r; + height = ViewSrg::m_heightmapImage.SampleLevel(ViewSrg::HeightmapSampler, uv, 0).r; } return ObjectSrg::m_terrainData.m_heightScale * (height - 0.5f); diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli new file mode 100644 index 0000000000..e5d5ff688d --- /dev/null +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli @@ -0,0 +1,250 @@ +/* + * 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 + +enum DetailTextureFlags +{ + UseTextureBaseColor = 0x00000001, //0b0000'0000'0000'0000'0000'0000'0000'0001 + UseTextureNormal = 0x00000002, //0b0000'0000'0000'0000'0000'0000'0000'0010 + UseTextureMetallic = 0x00000004, //0b0000'0000'0000'0000'0000'0000'0000'0100 + UseTextureRoughness = 0x00000008, //0b0000'0000'0000'0000'0000'0000'0000'1000 + UseTextureOcclusion = 0x00000010, //0b0000'0000'0000'0000'0000'0000'0001'0000 + UseTextureHeight = 0x00000020, //0b0000'0000'0000'0000'0000'0000'0010'0000 + UseTextureSpecularF0 = 0x00000040, //0b0000'0000'0000'0000'0000'0000'0100'0000 + + FlipNormalX = 0x00010000, //0b0000'0000'0000'0001'0000'0000'0000'0000 + FlipNormalY = 0x00020000, //0b0000'0000'0000'0010'0000'0000'0000'0000 + + BlendModeMask = 0x000C0000, //0b0000'0000'0000'1100'0000'0000'0000'0000 + BlendModeLerp = 0x00000000, //0b0000'0000'0000'0000'0000'0000'0000'0000 + BlendModeLinearLight = 0x00040000, //0b0000'0000'0000'0100'0000'0000'0000'0000 + BlendModeMultiply = 0x00080000, //0b0000'0000'0000'1000'0000'0000'0000'0000 + BlendModeOverlay = 0x000C0000, //0b0000'0000'0000'1100'0000'0000'0000'0000 +}; + +struct DetailSurface +{ + float3 m_color; + float3 m_normal; + float m_roughness; + float m_specularF0; + float m_metalness; + float m_occlusion; + float m_height; +}; + +option bool o_debugDetailMaterialIds = false; + +DetailSurface GetDefaultDetailSurface() +{ + DetailSurface surface; + + surface.m_color = float3(0.5, 0.5, 0.5); + surface.m_normal = float3(0.0, 0.0, 1.0); + surface.m_roughness = 1.0; + surface.m_specularF0 = 0.5; + surface.m_metalness = 0.0; + surface.m_occlusion = 1.0; + surface.m_height = 0.5; + + return surface; +} + +// Detail material index getters +uint GetDetailColorIndex(TerrainSrg::DetailMaterialData materialData) +{ + return materialData.m_colorNormalImageIndices & 0x0000FFFF; +} + +uint GetDetailNormalIndex(TerrainSrg::DetailMaterialData materialData) +{ + return materialData.m_colorNormalImageIndices >> 16; +} + +uint GetDetailRoughnessIndex(TerrainSrg::DetailMaterialData materialData) +{ + return materialData.m_roughnessMetalnessImageIndices & 0x0000FFFF; +} + +uint GetDetailMetalnessIndex(TerrainSrg::DetailMaterialData materialData) +{ + return materialData.m_roughnessMetalnessImageIndices >> 16; +} + +uint GetDetailSpecularF0Index(TerrainSrg::DetailMaterialData materialData) +{ + return materialData.m_specularF0OcclusionImageIndices & 0x0000FFFF; +} + +uint GetDetailOcclusionIndex(TerrainSrg::DetailMaterialData materialData) +{ + return materialData.m_specularF0OcclusionImageIndices >> 16; +} + +uint GetDetailHeightIndex(TerrainSrg::DetailMaterialData materialData) +{ + return materialData.m_heightImageIndex & 0x0000FFFF; +} + +// Detail material value getters + +float3 GetDetailColor(TerrainSrg::DetailMaterialData materialData, float2 uv) +{ + float3 color = materialData.m_baseColor; + if ((materialData.m_flags & DetailTextureFlags::UseTextureBaseColor) > 0) + { + color = TerrainSrg::m_detailTextures[GetDetailColorIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).rgb; + } + return color * materialData.m_baseColorFactor; +} + +float3 GetDetailNormal(TerrainSrg::DetailMaterialData materialData, float2 uv) +{ + float2 normal = float2(0.0, 0.0); + if ((materialData.m_flags & DetailTextureFlags::UseTextureNormal) > 0) + { + normal = TerrainSrg::m_detailTextures[GetDetailNormalIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).rg; + } + + // X and Y are inverted here to be consistent with SampleNormalXY in NormalInput.azsli. + if(materialData.m_flags & DetailTextureFlags::FlipNormalX) + { + normal.y = -normal.y; + } + if(materialData.m_flags & DetailTextureFlags::FlipNormalY) + { + normal.x = -normal.x; + } + return GetTangentSpaceNormal(normal, materialData.m_normalFactor); +} + +float GetDetailRoughness(TerrainSrg::DetailMaterialData materialData, float2 uv) +{ + float roughness = materialData.m_roughnessScale; + if ((materialData.m_flags & DetailTextureFlags::UseTextureRoughness) > 0) + { + roughness = TerrainSrg::m_detailTextures[GetDetailRoughnessIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r; + roughness = materialData.m_roughnessBias + roughness * materialData.m_roughnessScale; + } + return roughness; +} + +float GetDetailMetalness(TerrainSrg::DetailMaterialData materialData, float2 uv) +{ + float metalness = 1.0; + if ((materialData.m_flags & DetailTextureFlags::UseTextureMetallic) > 0) + { + metalness = TerrainSrg::m_detailTextures[GetDetailMetalnessIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r; + } + return metalness * materialData.m_metalFactor; +} + +float GetDetailSpecularF0(TerrainSrg::DetailMaterialData materialData, float2 uv) +{ + float specularF0 = 1.0; + if ((materialData.m_flags & DetailTextureFlags::UseTextureSpecularF0) > 0) + { + specularF0 = TerrainSrg::m_detailTextures[GetDetailSpecularF0Index(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r; + } + return specularF0 * materialData.m_specularF0Factor; +} + +float GetDetailOcclusion(TerrainSrg::DetailMaterialData materialData, float2 uv) +{ + float occlusion = 1.0; + if ((materialData.m_flags & DetailTextureFlags::UseTextureOcclusion) > 0) + { + occlusion = TerrainSrg::m_detailTextures[GetDetailOcclusionIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r; + } + return occlusion * materialData.m_occlusionFactor; +} + +float GetDetailHeight(TerrainSrg::DetailMaterialData materialData, float2 uv) +{ + float height = materialData.m_heightFactor; + if ((materialData.m_flags & DetailTextureFlags::UseTextureHeight) > 0) + { + height = TerrainSrg::m_detailTextures[GetDetailHeightIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r; + height = materialData.m_heightOffset + height * materialData.m_heightFactor; + } + return height; +} + +void GetDetailSurfaceForMaterial(inout DetailSurface surface, uint materialId, float2 uv) +{ + TerrainSrg::DetailMaterialData detailMaterialData = TerrainSrg::m_detailMaterialData[materialId]; + + surface.m_color = GetDetailColor(detailMaterialData, uv); + surface.m_normal = GetDetailNormal(detailMaterialData, uv); + surface.m_roughness = GetDetailRoughness(detailMaterialData, uv); + surface.m_specularF0 = GetDetailSpecularF0(detailMaterialData, uv); + surface.m_metalness = GetDetailMetalness(detailMaterialData, uv); + surface.m_occlusion = GetDetailOcclusion(detailMaterialData, uv); + surface.m_height = GetDetailHeight(detailMaterialData, uv); +} + +void GetDebugDetailSurface(inout DetailSurface surface, uint material1, uint material2, float blend, float2 idUv) +{ + float3 material1Color = float3(0.1, 0.1, 0.1); + float3 material2Color = float3(0.1, 0.1, 0.1); + + // Get a reasonably random hue for the material id + if (material1 != 255) + { + float hue1 = (material1 * 25043 % 256) / 256.0; + material1Color = HsvToRgb(float3(hue1, 1.0, 1.0)); + } + if (material2 != 255) + { + float hue2 = (material2 * 25043 % 256) / 256.0; + material2Color = HsvToRgb(float3(hue2, 1.0, 1.0)); + } + + surface.m_color = lerp(material1Color, material2Color, blend); + float seamBlend = 0.0; + const float halfLineWidth = 1.0 / 2048.0; + if (any(abs(idUv) % 1.0 < halfLineWidth) || any(abs(idUv) % 1.0 > 1.0 - halfLineWidth)) + { + seamBlend = 1.0; + } + surface.m_color = lerp(surface.m_color, float3(0.0, 0.0, 0.0), seamBlend); // draw texture seams + surface.m_color = pow(surface.m_color , 2.2); + + surface.m_normal = float3(0.0, 0.0, 1.0); + surface.m_roughness = 1.0; + surface.m_specularF0 = 0.5; + surface.m_metalness = 0.0; + surface.m_occlusion = 1.0; + surface.m_height = 0.5; +} + +bool GetDetailSurface(inout DetailSurface surface, float2 idUv, float2 uv) +{ + uint4 material1 = TerrainSrg::m_detailMaterialIdImage.GatherRed(TerrainSrg::DetailSampler, idUv, 0).xyzw; + uint4 material2 = TerrainSrg::m_detailMaterialIdImage.GatherGreen(TerrainSrg::DetailSampler, idUv, 0).xyzw; + + const float maxBlendAmount = 0xFF; + // convert integer of 0-255 to float of 0-1. + float4 blends = float4(TerrainSrg::m_detailMaterialIdImage.GatherBlue(TerrainSrg::DetailSampler, idUv, 0).xyzw) / maxBlendAmount; + + if (o_debugDetailMaterialIds) + { + GetDebugDetailSurface(surface, material1.x, material2.x, blends.x, idUv); + return true; + } + + if (material1.x == 0xFF) + { + return false; + } + + GetDetailSurfaceForMaterial(surface, material1.x, uv); + return true; +} diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl index 5a43fe1c37..6b68336a3f 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl @@ -7,8 +7,11 @@ */ #include + #include +#include #include +#include #include #include #include @@ -17,7 +20,6 @@ #include #include #include -#include struct VSOutput { @@ -28,8 +30,6 @@ struct VSOutput float3 m_shadowCoords[ViewSrg::MaxCascadeCount] : UV2; }; -option bool o_debugDetailMaterialIds = false; - VSOutput TerrainPBR_MainPassVS(VertexInput IN) { VSOutput OUT; @@ -71,9 +71,9 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN) { // ------- Surface ------- Surface surface; - - // Position surface.position = IN.m_worldPosition.xyz; + surface.vertexNormal = normalize(IN.m_normal); + float viewDistance = length(ViewSrg::m_worldPosition - surface.position); float detailFactor = saturate((viewDistance - TerrainMaterialSrg::m_detailFadeDistance) / max(TerrainMaterialSrg::m_detailFadeLength, EPSILON)); float2 detailUv = IN.m_uv * TerrainMaterialSrg::m_detailTextureMultiplier; @@ -83,92 +83,80 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN) // ------- Macro Color / Normal ------- float3 macroColor = TerrainMaterialSrg::m_baseColor.rgb; - [unroll] for (uint i = 0; i < 4 && (i < ObjectSrg::m_macroMaterialCount); ++i) + + // There's a bug that shows up with an NVidia GTX 1660 Super card happening on driver versions as recent as 496.49 (10/26/21) in which + // the IN.m_uv values will intermittently "flicker" to 0.0 after entering and exiting game mode. + // (See https://github.com/o3de/o3de/issues/5014) + // This bug has only shown up on PCs when using the DX12 RHI. It doesn't show up with Vulkan or when capturing frames with PIX or + // RenderDoc. Our best guess is that it is a driver bug. The workaround is to use the IN.m_uv values in a calculation prior to the + // point that we actually use them for macroUv below. The "if(any(!isnan(IN.m_uv)))" seems to be sufficient for the workaround. The + // if statement will always be true, but just the act of reading these values in the if statement makes the values stable. Removing + // the if statement causes the flickering to occur using the steps documented in the bug. + if (any(!isnan(IN.m_uv))) { - float2 macroUvMin = ObjectSrg::m_macroMaterialData[i].m_uvMin; - float2 macroUvMax = ObjectSrg::m_macroMaterialData[i].m_uvMax; - float2 macroUv = lerp(macroUvMin, macroUvMax, IN.m_uv); - if (macroUv.x >= 0.0 && macroUv.x <= 1.0 && macroUv.y >= 0.0 && macroUv.y <= 1.0) + [unroll] for (uint i = 0; i < 4 && (i < ObjectSrg::m_macroMaterialCount); ++i) { - if ((ObjectSrg::m_macroMaterialData[i].m_mapsInUse & 1) > 0) + float2 macroUvMin = ObjectSrg::m_macroMaterialData[i].m_uvMin; + float2 macroUvMax = ObjectSrg::m_macroMaterialData[i].m_uvMax; + float2 macroUv = lerp(macroUvMin, macroUvMax, IN.m_uv); + if (macroUv.x >= 0.0 && macroUv.x <= 1.0 && macroUv.y >= 0.0 && macroUv.y <= 1.0) { - macroColor = GetBaseColorInput(ObjectSrg::m_macroColorMap[i], TerrainMaterialSrg::m_sampler, macroUv, macroColor, true); + if ((ObjectSrg::m_macroMaterialData[i].m_mapsInUse & 1) > 0) + { + macroColor = GetBaseColorInput(ObjectSrg::m_macroColorMap[i], TerrainMaterialSrg::m_sampler, macroUv, macroColor, true); + } + if ((ObjectSrg::m_macroMaterialData[i].m_mapsInUse & 2) > 0) + { + bool flipX = ObjectSrg::m_macroMaterialData[i].m_flipNormalX; + bool flipY = ObjectSrg::m_macroMaterialData[i].m_flipNormalY; + bool factor = ObjectSrg::m_macroMaterialData[i].m_normalFactor; + macroNormal = GetNormalInputTS(ObjectSrg::m_macroNormalMap[i], TerrainMaterialSrg::m_sampler, + macroUv, flipX, flipY, CreateIdentity3x3(), true, factor); + } + break; } - if ((ObjectSrg::m_macroMaterialData[i].m_mapsInUse & 2) > 0) - { - bool flipX = ObjectSrg::m_macroMaterialData[i].m_flipNormalX; - bool flipY = ObjectSrg::m_macroMaterialData[i].m_flipNormalY; - bool factor = ObjectSrg::m_macroMaterialData[i].m_normalFactor; - macroNormal = GetNormalInputTS(ObjectSrg::m_macroNormalMap[i], TerrainMaterialSrg::m_sampler, - macroUv, flipX, flipY, CreateIdentity3x3(), true, factor); - } - break; } } - - float3 detailNormal = GetNormalInputTS(TerrainMaterialSrg::m_normalMap, TerrainMaterialSrg::m_sampler, - detailUv, TerrainMaterialSrg::m_flipNormalX, TerrainMaterialSrg::m_flipNormalY, CreateIdentity3x3(), o_normal_useTexture, TerrainMaterialSrg::m_normalFactor); - - detailNormal = ReorientTangentSpaceNormal(macroNormal, detailNormal); - surface.normal = lerp(detailNormal, macroNormal, detailFactor); - surface.normal = normalize(surface.normal); - surface.vertexNormal = normalize(IN.m_normal); - + // ------- Base Color ------- - float3 detailColor = GetBaseColorInput(TerrainMaterialSrg::m_baseColorMap, TerrainMaterialSrg::m_sampler, detailUv, TerrainMaterialSrg::m_baseColor.rgb, o_baseColor_useTexture); - float3 blendedColor = BlendBaseColor(lerp(detailColor, TerrainMaterialSrg::m_baseColor.rgb, detailFactor), macroColor, TerrainMaterialSrg::m_baseColorFactor, o_baseColorTextureBlendMode, o_baseColor_useTexture); - // ------- Debug detail materials using random colors ------- - // This assigns a random color to each material, turns off any kind of distance fading, and draws a black line at the texture edges. - if (o_debugDetailMaterialIds) + DetailSurface detailSurface = GetDefaultDetailSurface(); + float2 detailRegionMin = TerrainSrg::m_detailAabb.xy; + float2 detailRegionMax = TerrainSrg::m_detailAabb.zw; + float2 detailRegionUv = (surface.position.xy - detailRegionMin) / (detailRegionMax - detailRegionMin); + bool hasDetailSurface = false; + + // Check to make sure we're inside the detail texture's bounds and within where detail textures should be drawn. + if (detailFactor < 1.0 && all(detailRegionUv > TerrainSrg::m_detailHalfPixelUv) && all(detailRegionUv < 1.0 - TerrainSrg::m_detailHalfPixelUv)) { - float2 detailRegionMin = TerrainMaterialSrg::m_detailAabb.xy; - float2 detailRegionMax = TerrainMaterialSrg::m_detailAabb.zw; - float2 detailRegionUv = (surface.position.xy - detailRegionMin) / (detailRegionMax - detailRegionMin); - if (all(detailRegionUv > TerrainMaterialSrg::m_detailHalfPixelUv) && all(detailRegionUv < 1.0 - TerrainMaterialSrg::m_detailHalfPixelUv)) - { - detailRegionUv += TerrainMaterialSrg::m_detailMaterialIdImageCenter - (0.5); - - uint material1 = TerrainMaterialSrg::m_detailMaterialIdImage.GatherRed(TerrainMaterialSrg::m_detailSampler, detailRegionUv, 0).r; - uint material2 = TerrainMaterialSrg::m_detailMaterialIdImage.GatherGreen(TerrainMaterialSrg::m_detailSampler, detailRegionUv, 0).r; - float blend = float(TerrainMaterialSrg::m_detailMaterialIdImage.GatherBlue(TerrainMaterialSrg::m_detailSampler, detailRegionUv, 0).r) / 0xFF; - - float3 material1Color = float3(0.1, 0.1, 0.1); - float3 material2Color = float3(0.1, 0.1, 0.1); - - // Get a reasonably random hue for the material id - if (material1 != 255) - { - float hue1 = (material1 * 25043 % 256) / 256.0; - material1Color = HsvToRgb(float3(hue1, 1.0, 1.0)); - } - if (material2 != 255) - { - float hue2 = (material2 * 25043 % 256) / 256.0; - material2Color = HsvToRgb(float3(hue2, 1.0, 1.0)); - } - - blendedColor = lerp(material1Color, material2Color, blend); - float seamBlend = 0.0; - const float halfLineWidth = 1.0 / 2048.0; - if (any(abs(detailRegionUv) % 1.0 < halfLineWidth) || any(abs(detailRegionUv) % 1.0 > 1.0 - halfLineWidth)) - { - seamBlend = 1.0; - } - blendedColor = lerp(blendedColor, float3(0.0, 0.0, 0.0), seamBlend); // draw texture seams - blendedColor = pow(blendedColor , 2.2); - } + detailRegionUv += TerrainSrg::m_detailMaterialIdImageCenter - (0.5); + hasDetailSurface = GetDetailSurface(detailSurface, detailRegionUv, detailUv); } - // ------- Specular ------- - float specularF0Factor = GetSpecularInput(TerrainMaterialSrg::m_specularF0Map, TerrainMaterialSrg::m_sampler, detailUv, TerrainMaterialSrg::m_specularF0Factor, o_specularF0_useTexture); - specularF0Factor = lerp(specularF0Factor, 0.5, detailFactor); - surface.SetAlbedoAndSpecularF0(blendedColor, specularF0Factor, 0.0); + const float macroRoughness = 1.0; + const float macroSpecularF0 = 0.5; + const float macroMetalness = 0.0; - // ------- Roughness ------- - surface.roughnessLinear = GetRoughnessInput(TerrainMaterialSrg::m_roughnessMap, TerrainMaterialSrg::m_sampler, detailUv, TerrainMaterialSrg::m_roughnessFactor, 0.0, 1.0, o_roughness_useTexture); - surface.roughnessLinear = lerp(surface.roughnessLinear, 1.0, detailFactor); - surface.CalculateRoughnessA(); + if (hasDetailSurface) + { + float3 blendedColor = lerp(detailSurface.m_color, macroColor, detailFactor); + float blendedSpecularF0 = lerp(detailSurface.m_specularF0, macroSpecularF0, detailFactor); + surface.SetAlbedoAndSpecularF0(blendedColor, blendedSpecularF0, detailSurface.m_metalness * (1.0 - detailFactor)); + + surface.roughnessLinear = lerp(detailSurface.m_roughness, macroRoughness, detailFactor); + surface.CalculateRoughnessA(); + + detailSurface.m_normal = ReorientTangentSpaceNormal(macroNormal, detailSurface.m_normal); + surface.normal = lerp(detailSurface.m_normal, macroNormal, detailFactor); + surface.normal = normalize(surface.normal); + } + else + { + surface.normal = macroNormal; + surface.SetAlbedoAndSpecularF0(macroColor, macroSpecularF0, macroMetalness); + surface.roughnessLinear = macroRoughness; + surface.CalculateRoughnessA(); + } // Clear Coat, Transmission (Not used for terrain) surface.clearCoat.InitializeToZero(); @@ -184,6 +172,7 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN) // Shadow, Occlusion lightingData.shadowCoords = IN.m_shadowCoords; + lightingData.diffuseAmbientOcclusion = detailSurface.m_occlusion; // Diffuse and Specular response lightingData.specularResponse = FresnelSchlickWithRoughness(lightingData.NdotV, surface.specularF0, surface.roughnessLinear); diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainSrg.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainSrg.azsli new file mode 100644 index 0000000000..c8ab04a5bc --- /dev/null +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainSrg.azsli @@ -0,0 +1,74 @@ +/* + * 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 + +ShaderResourceGroupSemantic SRG_Terrain +{ + FrequencyId = 7; +}; + +ShaderResourceGroup TerrainSrg : SRG_Terrain +{ + + Sampler DetailSampler + { + AddressU = Wrap; + AddressV = Wrap; + MinFilter = Point; + MagFilter = Point; + MipFilter = Point; + }; + + struct DetailMaterialData + { + // Uv + row_major float3x4 m_uvTransform; + + float3 m_baseColor; + + // Factor / Scale / Bias for input textures + float m_baseColorFactor; + + float m_normalFactor; + float m_metalFactor; + float m_roughnessScale; + float m_roughnessBias; + + float m_specularF0Factor; + float m_occlusionFactor; + float m_heightFactor; + float m_heightOffset; + + float m_heightBlendFactor; + + // Flags + uint m_flags; // see DetailTextureFlags + + // Image indices + uint m_colorNormalImageIndices; + uint m_roughnessMetalnessImageIndices; + + uint m_specularF0OcclusionImageIndices; + uint m_heightImageIndex; // only first 16 bits used + + // 16 byte aligned + uint2 m_padding; + }; + + Texture2D m_detailMaterialIdImage; + StructuredBuffer m_detailMaterialData; + Texture2D m_detailTextures[]; // bindless array of all textures for detail materials + + float2 m_detailMaterialIdImageCenter; + float m_detailHalfPixelUv; + float4 m_detailAabb; + +} diff --git a/Gems/Terrain/Assets/Shaders/Terrain/ViewSrg.azsli b/Gems/Terrain/Assets/Shaders/Terrain/ViewSrg.azsli new file mode 100644 index 0000000000..144f2abf6b --- /dev/null +++ b/Gems/Terrain/Assets/Shaders/Terrain/ViewSrg.azsli @@ -0,0 +1,80 @@ +/* + * 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 + * + */ + +#ifndef AZ_COLLECTING_PARTIAL_SRGS +#error Do not include this file directly. Include the main .srgi file instead. +#endif + +partial ShaderResourceGroup ViewSrg +{ + Sampler HeightmapSampler + { + MinFilter = Linear; + MagFilter = Linear; + MipFilter = Point; + AddressU = Clamp; + AddressV = Clamp; + AddressW = Clamp; + }; + + Sampler DetailSampler + { + AddressU = Wrap; + AddressV = Wrap; + MinFilter = Point; + MagFilter = Point; + MipFilter = Point; + }; + + struct DetailMaterialData + { + // Uv + row_major float3x4 m_uvTransform; + + float3 m_baseColor; + + // Factor / Scale / Bias for input textures + float m_baseColorFactor; + + float m_normalFactor; + float m_metalFactor; + float m_roughnessScale; + float m_roughnessBias; + + float m_specularF0Factor; + float m_occlusionFactor; + float m_heightFactor; + float m_heightOffset; + + float m_heightBlendFactor; + + // Flags + uint m_flags; // see DetailTextureFlags + + // Image indices + uint m_colorNormalImageIndices; + uint m_roughnessMetalnessImageIndices; + + uint m_specularF0OcclusionImageIndices; + uint m_heightImageIndex; // only first 16 bits used + + // 16 byte aligned + uint2 m_padding; + }; + + Texture2D m_heightmapImage; + Texture2D m_detailMaterialIdImage; + StructuredBuffer m_detailMaterialData; + + Texture2D m_detailTextures[]; // bindless array of all textures for detail materials + + float2 m_detailMaterialIdImageCenter; + float m_detailHalfPixelUv; + + float4 m_detailAabb; +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index 716c900f9b..9f83844243 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -31,6 +31,9 @@ #include #include #include +#include +#include +#include #include #include @@ -50,18 +53,24 @@ namespace Terrain const char* TerrainDetailChars = "TerrainDetail"; } - namespace MaterialInputs + namespace ViewSrgInputs { - // Terrain material - static const char* const HeightmapImage("settings.heightmapImage"); - static const char* const DetailMaterialIdImage("settings.detailMaterialIdImage"); - static const char* const DetailCenter("settings.detailMaterialIdCenter"); - static const char* const DetailAabb("settings.detailAabb"); - static const char* const DetailHalfPixelUv("settings.detailHalfPixelUv"); + static const char* const HeightmapImage("m_heightmapImage"); + } + + namespace TerrainSrgInputs + { + static const char* const DetailMaterialIdImage("m_detailMaterialIdImage"); + static const char* const DetailMaterialData("m_detailMaterialData"); + static const char* const DetailMaterialIdImageCenter("m_detailMaterialIdImageCenter"); + static const char* const DetailHalfPixelUv("m_detailHalfPixelUv"); + static const char* const DetailAabb("m_detailAabb"); + static const char* const DetailTextures("m_detailTextures"); } namespace DetailMaterialInputs { + static const char* const BaseColorColor("baseColor.color"); static const char* const BaseColorMap("baseColor.textureMap"); static const char* const BaseColorUseTexture("baseColor.useTexture"); static const char* const BaseColorFactor("baseColor.factor"); @@ -72,8 +81,8 @@ namespace Terrain static const char* const RoughnessMap("roughness.textureMap"); static const char* const RoughnessUseTexture("roughness.useTexture"); static const char* const RoughnessFactor("roughness.factor"); - static const char* const RoughnessUpperBound("roughness.lowerBound"); - static const char* const RoughnessLowerBound("roughness.upperBound"); + static const char* const RoughnessLowerBound("roughness.lowerBound"); + static const char* const RoughnessUpperBound("roughness.upperBound"); static const char* const SpecularF0Map("specularF0.textureMap"); static const char* const SpecularF0UseTexture("specularF0.useTexture"); static const char* const SpecularF0Factor("specularF0.factor"); @@ -126,6 +135,9 @@ namespace Terrain void TerrainFeatureProcessor::Activate() { + EnableSceneNotification(); + CacheForwardPass(); + Initialize(); AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); @@ -138,6 +150,13 @@ namespace Terrain void TerrainFeatureProcessor::Initialize() { + // Load indices for the View Srg. + + auto viewSrgLayout = AZ::RPI::RPISystemInterface::Get()->GetViewSrgLayout(); + + m_heightmapPropertyIndex = viewSrgLayout->FindShaderInputImageIndex(AZ::Name(ViewSrgInputs::HeightmapImage)); + AZ_Error(TerrainFPName, m_heightmapPropertyIndex.IsValid(), "Failed to find view srg input constant %s.", ViewSrgInputs::HeightmapImage); + // Load the terrain material asynchronously const AZStd::string materialFilePath = "Materials/Terrain/DefaultPbrTerrain.azmaterial"; m_materialAssetLoader = AZStd::make_unique(); @@ -166,6 +185,7 @@ namespace Terrain return; } OnTerrainDataChanged(AZ::Aabb::CreateNull(), TerrainDataChangedMask::HeightData); + } void TerrainFeatureProcessor::Deactivate() @@ -173,6 +193,8 @@ namespace Terrain TerrainMacroMaterialNotificationBus::Handler::BusDisconnect(); AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); AZ::RPI::MaterialReloadNotificationBus::Handler::BusDisconnect(); + + DisableSceneNotification(); m_patchModel = {}; m_areaData = {}; @@ -181,6 +203,7 @@ namespace Terrain m_macroMaterials.Clear(); m_materialAssetLoader = {}; m_materialInstance = {}; + } void TerrainFeatureProcessor::Render(const AZ::RPI::FeatureProcessor::RenderPacket& packet) @@ -339,9 +362,47 @@ namespace Terrain uint16_t detailMaterialId = CreateOrUpdateDetailMaterial(material); materialRegion.m_materialsForSurfaces.push_back({ surfaceTag, detailMaterialId }); + m_detailMaterials.GetData(detailMaterialId).refCount++; m_dirtyDetailRegion.AddAabb(materialRegion.m_region); } + void TerrainFeatureProcessor::OnRenderPipelinePassesChanged([[maybe_unused]] AZ::RPI::RenderPipeline* renderPipeline) + { + CacheForwardPass(); + } + + void TerrainFeatureProcessor::CheckDetailMaterialForDeletion(uint16_t detailMaterialId) + { + auto& detailMaterialData = m_detailMaterials.GetData(detailMaterialId); + if (--detailMaterialData.refCount == 0) + { + uint16_t bufferIndex = detailMaterialData.m_detailMaterialBufferIndex; + DetailMaterialShaderData& shaderData = m_detailMaterialShaderData.GetElement(bufferIndex); + + for (uint16_t imageIndex : + { + shaderData.m_colorImageIndex, + shaderData.m_normalImageIndex, + shaderData.m_roughnessImageIndex, + shaderData.m_metalnessImageIndex, + shaderData.m_specularF0ImageIndex, + shaderData.m_occlusionImageIndex, + shaderData.m_heightImageIndex + }) + { + if (imageIndex != InvalidDetailImageIndex) + { + m_detailImageViews.at(imageIndex) = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::Magenta)->GetImageView(); + m_detailImageViewFreeList.push_back(imageIndex); + m_detailImagesNeedUpdate = true; + } + } + + m_detailMaterialShaderData.Release(bufferIndex); + m_detailMaterials.RemoveIndex(detailMaterialId); + } + } + void TerrainFeatureProcessor::OnTerrainSurfaceMaterialMappingDestroyed(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag) { DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); @@ -350,6 +411,8 @@ namespace Terrain { if (surface.m_surfaceTag == surfaceTag) { + CheckDetailMaterialForDeletion(surface.m_detailMaterialId); + if (surface.m_surfaceTag != materialRegion.m_materialsForSurfaces.back().m_surfaceTag) { AZStd::swap(surface, materialRegion.m_materialsForSurfaces.back()); @@ -373,13 +436,19 @@ namespace Terrain if (surface.m_surfaceTag == surfaceTag) { found = true; - surface.m_detailMaterialId = materialId; + if (surface.m_detailMaterialId != materialId) + { + ++m_detailMaterials.GetData(materialId).refCount; + CheckDetailMaterialForDeletion(surface.m_detailMaterialId); + surface.m_detailMaterialId = materialId; + } break; } } if (!found) { + ++m_detailMaterials.GetData(materialId).refCount; materialRegion.m_materialsForSurfaces.push_back({ surfaceTag, materialId }); } m_dirtyDetailRegion.AddAabb(materialRegion.m_region); @@ -398,138 +467,196 @@ namespace Terrain static constexpr uint16_t InvalidDetailMaterial = 0xFFFF; uint16_t detailMaterialId = InvalidDetailMaterial; - for (DetailMaterialData& detailMaterial : m_detailMaterials.GetDataVector()) + for (auto& detailMaterialData : m_detailMaterials.GetDataVector()) { - if (detailMaterial.m_assetId == material->GetAssetId()) + if (detailMaterialData.m_assetId == material->GetAssetId()) { - UpdateDetailMaterialData(detailMaterial, material); - detailMaterialId = m_detailMaterials.GetIndexForData(&detailMaterial); + detailMaterialId = m_detailMaterials.GetIndexForData(&detailMaterialData); + UpdateDetailMaterialData(detailMaterialId, material); break; } } - if (detailMaterialId == InvalidDetailMaterial) + AZ_Assert(m_detailMaterialShaderData.GetSize() < 0xFF, "Only 255 detail materials supported."); + + if (detailMaterialId == InvalidDetailMaterial && m_detailMaterialShaderData.GetSize() < 0xFF) { detailMaterialId = m_detailMaterials.GetFreeSlotIndex(); - UpdateDetailMaterialData(m_detailMaterials.GetData(detailMaterialId), material); + auto& detailMaterialData = m_detailMaterials.GetData(detailMaterialId); + detailMaterialData.m_detailMaterialBufferIndex = aznumeric_cast(m_detailMaterialShaderData.Reserve()); + UpdateDetailMaterialData(detailMaterialId, material); } return detailMaterialId; } - void TerrainFeatureProcessor::UpdateDetailMaterialData(DetailMaterialData& materialData, MaterialInstance material) + void TerrainFeatureProcessor::UpdateDetailMaterialData(uint16_t detailMaterialIndex, MaterialInstance material) { - if (materialData.m_materialChangeId != material->GetCurrentChangeId()) + DetailMaterialData& materialData = m_detailMaterials.GetData(detailMaterialIndex); + DetailMaterialShaderData& shaderData = m_detailMaterialShaderData.GetElement(materialData.m_detailMaterialBufferIndex); + + if (materialData.m_materialChangeId == material->GetCurrentChangeId()) { - materialData = DetailMaterialData(); - DetailTextureFlags& flags = materialData.m_properties.m_flags; - materialData.m_materialChangeId = material->GetCurrentChangeId(); - materialData.m_assetId = material->GetAssetId(); - - auto getIndex = [&](const char* const indexName) -> AZ::RPI::MaterialPropertyIndex - { - const AZ::RPI::MaterialPropertyIndex index = material->FindPropertyIndex(AZ::Name(indexName)); - AZ_Warning(TerrainFPName, index.IsValid(), "Failed to find shader input constant %s.", indexName); - return index; - }; - - auto applyProperty = [&](const char* const indexName, auto& ref) -> void - { - const auto index = getIndex(indexName); - if (index.IsValid()) - { - using TypeRefRemoved = AZStd::remove_cvref_t; - ref = material->GetPropertyValue(index).GetValue(); - } - }; - - auto applyFlag = [&](const char* const indexName, DetailTextureFlags flagToSet) -> void - { - const auto index = getIndex(indexName); - if (index.IsValid()) - { - bool flagValue = material->GetPropertyValue(index).GetValue(); - flags = DetailTextureFlags(flagValue ? flags | flagToSet : flags); - } - }; - - auto getEnumName = [&](const char* const indexName) -> const AZStd::string_view - { - const auto index = getIndex(indexName); - if (index.IsValid()) - { - uint32_t enumIndex = material->GetPropertyValue(index).GetValue(); - const AZ::Name& enumName = material->GetMaterialPropertiesLayout()->GetPropertyDescriptor(index)->GetEnumName(enumIndex); - return enumName.GetStringView(); - } - return ""; - }; - - using namespace DetailMaterialInputs; - applyProperty(BaseColorMap, materialData.m_colorImage); - applyFlag(BaseColorUseTexture, DetailTextureFlags::UseTextureBaseColor); - applyProperty(BaseColorFactor, materialData.m_properties.m_baseColorFactor); - - const AZStd::string_view& blendModeString = getEnumName(BaseColorBlendMode); - if (blendModeString == "Multiply") - { - flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeMultiply); - } - else if (blendModeString == "LinearLight") - { - flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeLinearLight); - } - else if (blendModeString == "Lerp") - { - flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeLerp); - } - else if (blendModeString == "Overlay") - { - flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeOverlay); - } - - applyProperty(MetallicMap, materialData.m_metalnessImage); - applyFlag(MetallicUseTexture, DetailTextureFlags::UseTextureMetallic); - applyProperty(MetallicFactor, materialData.m_properties.m_metalFactor); - - applyProperty(RoughnessMap, materialData.m_roughnessImage); - applyFlag(RoughnessUseTexture, DetailTextureFlags::UseTextureRoughness); - - if ((flags & DetailTextureFlags::UseTextureRoughness) > 0) - { - float lowerBound = 0.0; - float upperBound = 1.0; - applyProperty(RoughnessLowerBound, lowerBound); - applyProperty(RoughnessUpperBound, upperBound); - materialData.m_properties.m_roughnessBias = lowerBound; - materialData.m_properties.m_roughnessScale = upperBound - lowerBound; - } - else - { - materialData.m_properties.m_roughnessBias = 0.0; - applyProperty(RoughnessFactor, materialData.m_properties.m_roughnessScale); - } - - applyProperty(SpecularF0Map, materialData.m_specularF0Image); - applyFlag(SpecularF0UseTexture, DetailTextureFlags::UseTextureSpecularF0); - applyProperty(SpecularF0Factor, materialData.m_properties.m_specularF0Factor); - - applyProperty(NormalMap, materialData.m_normalImage); - applyFlag(NormalUseTexture, DetailTextureFlags::UseTextureNormal); - applyProperty(NormalFactor, materialData.m_properties.m_normalFactor); - applyFlag(NormalFlipX, DetailTextureFlags::FlipNormalX); - applyFlag(NormalFlipY, DetailTextureFlags::FlipNormalY); - - applyProperty(DiffuseOcclusionMap, materialData.m_occlusionImage); - applyFlag(DiffuseOcclusionUseTexture, DetailTextureFlags::UseTextureOcclusion); - applyProperty(DiffuseOcclusionFactor, materialData.m_properties.m_occlusionFactor); - - applyProperty(HeightMap, materialData.m_heightImage); - applyFlag(HeightUseTexture, DetailTextureFlags::UseTextureHeight); - applyProperty(HeightFactor, materialData.m_properties.m_heightFactor); - applyProperty(HeightOffset, materialData.m_properties.m_heightOffset); - applyProperty(HeightBlendFactor, materialData.m_properties.m_heightBlendFactor); - + return; // material hasn't changed, nothing to do } + + materialData.m_materialChangeId = material->GetCurrentChangeId(); + materialData.m_assetId = material->GetAssetId(); + + DetailTextureFlags& flags = shaderData.m_flags; + + auto getIndex = [&](const char* const indexName) -> AZ::RPI::MaterialPropertyIndex + { + const AZ::RPI::MaterialPropertyIndex index = material->FindPropertyIndex(AZ::Name(indexName)); + AZ_Warning(TerrainFPName, index.IsValid(), "Failed to find shader input constant %s.", indexName); + return index; + }; + + auto applyProperty = [&](const char* const indexName, auto& ref) -> void + { + const auto index = getIndex(indexName); + if (index.IsValid()) + { + // GetValue() expects the actaul type, not a reference type, so the reference needs to be removed. + using TypeRefRemoved = AZStd::remove_cvref_t; + ref = material->GetPropertyValue(index).GetValue(); + } + }; + + auto applyImage = [&](const char* const indexName, AZ::Data::Instance& ref, const char* const usingFlagName, DetailTextureFlags flagToSet, uint16_t& imageIndex) -> void + { + // Determine if an image exists and if its using flag allows it to be used. + const auto index = getIndex(indexName); + const auto useTextureIndex = getIndex(usingFlagName); + bool useTextureValue = true; + if (useTextureIndex.IsValid()) + { + useTextureValue = material->GetPropertyValue(useTextureIndex).GetValue(); + } + if (index.IsValid() && useTextureValue) + { + ref = material->GetPropertyValue(index).GetValue>(); + } + useTextureValue = useTextureValue && ref; + flags = DetailTextureFlags(useTextureValue ? (flags | flagToSet) : (flags & ~flagToSet)); + + // Update queues to add/remove textures depending on if the image is used + if (ref) + { + if (imageIndex == InvalidDetailImageIndex) + { + if (m_detailImageViewFreeList.size() > 0) + { + imageIndex = m_detailImageViewFreeList.back(); + m_detailImageViewFreeList.pop_back(); + } + else + { + imageIndex = aznumeric_cast(m_detailImageViews.size()); + m_detailImageViews.push_back(); + } + } + m_detailImageViews.at(imageIndex) = ref->GetImageView(); + m_detailImagesNeedUpdate = true; + } + else if (imageIndex != InvalidDetailImageIndex) + { + m_detailImageViews.at(imageIndex) = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::Magenta)->GetImageView(); + m_detailImageViewFreeList.push_back(imageIndex); + m_detailImagesNeedUpdate = true; + imageIndex = InvalidDetailImageIndex; + } + }; + + auto applyFlag = [&](const char* const indexName, DetailTextureFlags flagToSet) -> void + { + const auto index = getIndex(indexName); + if (index.IsValid()) + { + bool flagValue = material->GetPropertyValue(index).GetValue(); + flags = DetailTextureFlags(flagValue ? flags | flagToSet : flags); + } + }; + + auto getEnumName = [&](const char* const indexName) -> const AZStd::string_view + { + const auto index = getIndex(indexName); + if (index.IsValid()) + { + uint32_t enumIndex = material->GetPropertyValue(index).GetValue(); + const AZ::Name& enumName = material->GetMaterialPropertiesLayout()->GetPropertyDescriptor(index)->GetEnumName(enumIndex); + return enumName.GetStringView(); + } + return ""; + }; + + using namespace DetailMaterialInputs; + applyImage(BaseColorMap, materialData.m_colorImage, BaseColorUseTexture, DetailTextureFlags::UseTextureBaseColor, shaderData.m_colorImageIndex); + applyProperty(BaseColorFactor, shaderData.m_baseColorFactor); + + const auto index = getIndex(BaseColorColor); + if (index.IsValid()) + { + AZ::Color baseColor = material->GetPropertyValue(index).GetValue(); + shaderData.m_baseColorRed = baseColor.GetR(); + shaderData.m_baseColorGreen = baseColor.GetG(); + shaderData.m_baseColorBlue = baseColor.GetB(); + } + + const AZStd::string_view& blendModeString = getEnumName(BaseColorBlendMode); + if (blendModeString == "Multiply") + { + flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeMultiply); + } + else if (blendModeString == "LinearLight") + { + flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeLinearLight); + } + else if (blendModeString == "Lerp") + { + flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeLerp); + } + else if (blendModeString == "Overlay") + { + flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeOverlay); + } + + applyImage(MetallicMap, materialData.m_metalnessImage, MetallicUseTexture, DetailTextureFlags::UseTextureMetallic, shaderData.m_metalnessImageIndex); + applyProperty(MetallicFactor, shaderData.m_metalFactor); + + applyImage(RoughnessMap, materialData.m_roughnessImage, RoughnessUseTexture, DetailTextureFlags::UseTextureRoughness, shaderData.m_roughnessImageIndex); + + if ((flags & DetailTextureFlags::UseTextureRoughness) > 0) + { + float lowerBound = 0.0; + float upperBound = 1.0; + applyProperty(RoughnessLowerBound, lowerBound); + applyProperty(RoughnessUpperBound, upperBound); + shaderData.m_roughnessBias = lowerBound; + shaderData.m_roughnessScale = upperBound - lowerBound; + } + else + { + shaderData.m_roughnessBias = 0.0; + applyProperty(RoughnessFactor, shaderData.m_roughnessScale); + } + + applyImage(SpecularF0Map, materialData.m_specularF0Image, SpecularF0UseTexture, DetailTextureFlags::UseTextureSpecularF0, shaderData.m_specularF0ImageIndex); + applyProperty(SpecularF0Factor, shaderData.m_specularF0Factor); + + applyImage(NormalMap, materialData.m_normalImage, NormalUseTexture, DetailTextureFlags::UseTextureNormal, shaderData.m_normalImageIndex); + applyProperty(NormalFactor, shaderData.m_normalFactor); + applyFlag(NormalFlipX, DetailTextureFlags::FlipNormalX); + applyFlag(NormalFlipY, DetailTextureFlags::FlipNormalY); + + applyImage(DiffuseOcclusionMap, materialData.m_occlusionImage, DiffuseOcclusionUseTexture, DetailTextureFlags::UseTextureOcclusion, shaderData.m_occlusionImageIndex); + applyProperty(DiffuseOcclusionFactor, shaderData.m_occlusionFactor); + + applyImage(HeightMap, materialData.m_heightImage, HeightUseTexture, DetailTextureFlags::UseTextureHeight, shaderData.m_heightImageIndex); + applyProperty(HeightFactor, shaderData.m_heightFactor); + applyProperty(HeightOffset, shaderData.m_heightOffset); + applyProperty(HeightBlendFactor, shaderData.m_heightBlendFactor); + + m_updateDetailMaterialBuffer = true; } void TerrainFeatureProcessor::CheckUpdateDetailTexture(const Aabb2i& newBounds, const Vector2i& newCenter) @@ -765,7 +892,7 @@ namespace Terrain { if (materialSurface.m_surfaceTag == surfaceType) { - return materialSurface.m_detailMaterialId; + return m_detailMaterials.GetData(materialSurface.m_detailMaterialId).m_detailMaterialBufferIndex; } } } @@ -801,6 +928,7 @@ namespace Terrain // World size changed, so the whole height map needs updating. m_dirtyRegion = worldBounds; + m_imagesNeedUpdate = true; } int32_t xStart = aznumeric_cast(AZStd::ceilf(m_dirtyRegion.GetMin().GetX() / queryResolution)); @@ -889,21 +1017,48 @@ namespace Terrain m_macroNormalMapIndex = layout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::MacroNormalMap)); AZ_Error(TerrainFPName, m_macroNormalMapIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::MacroNormalMap); - - m_heightmapPropertyIndex = m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::HeightmapImage)); - AZ_Error(TerrainFPName, m_heightmapPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::HeightmapImage); - - m_detailMaterialIdPropertyIndex = m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::DetailMaterialIdImage)); - AZ_Error(TerrainFPName, m_detailMaterialIdPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::DetailMaterialIdImage); - - m_detailCenterPropertyIndex = m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::DetailCenter)); - AZ_Error(TerrainFPName, m_detailCenterPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::DetailCenter); - m_detailAabbPropertyIndex = m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::DetailAabb)); - AZ_Error(TerrainFPName, m_detailAabbPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::DetailAabb); + m_terrainSrg = {}; + for (auto& shaderItem : m_materialInstance->GetShaderCollection()) + { + if (shaderItem.GetShaderAsset()->GetDrawListName() == AZ::Name("forward")) + { + const auto& shaderAsset = shaderItem.GetShaderAsset(); + m_terrainSrg = AZ::RPI::ShaderResourceGroup::Create(shaderItem.GetShaderAsset(), shaderAsset->GetSupervariantIndex(AZ::Name()), AZ::Name{"TerrainSrg"}); + AZ_Error(TerrainFPName, m_terrainSrg, "Failed to create Terrain shader resource group"); + break; + } + } + + AZ_Error(TerrainFPName, m_terrainSrg, "Terrain Srg not found on any shader in the terrain material"); + + if (m_terrainSrg) + { + const AZ::RHI::ShaderResourceGroupLayout* terrainSrgLayout = m_terrainSrg->GetLayout(); + + m_detailMaterialIdPropertyIndex = terrainSrgLayout->FindShaderInputImageIndex(AZ::Name(TerrainSrgInputs::DetailMaterialIdImage)); + AZ_Error(TerrainFPName, m_detailMaterialIdPropertyIndex.IsValid(), "Failed to find view srg input constant %s.", TerrainSrgInputs::DetailMaterialIdImage); - m_detailHalfPixelUvPropertyIndex = m_materialInstance->GetMaterialPropertiesLayout()->FindPropertyIndex(AZ::Name(MaterialInputs::DetailHalfPixelUv)); - AZ_Error(TerrainFPName, m_detailHalfPixelUvPropertyIndex.IsValid(), "Failed to find material input constant %s.", MaterialInputs::DetailHalfPixelUv); + m_detailCenterPropertyIndex = terrainSrgLayout->FindShaderInputConstantIndex(AZ::Name(TerrainSrgInputs::DetailMaterialIdImageCenter)); + AZ_Error(TerrainFPName, m_detailCenterPropertyIndex.IsValid(), "Failed to find view srg input constant %s.", TerrainSrgInputs::DetailMaterialIdImageCenter); + + m_detailHalfPixelUvPropertyIndex = terrainSrgLayout->FindShaderInputConstantIndex(AZ::Name(TerrainSrgInputs::DetailHalfPixelUv)); + AZ_Error(TerrainFPName, m_detailHalfPixelUvPropertyIndex.IsValid(), "Failed to find view srg input constant %s.", TerrainSrgInputs::DetailHalfPixelUv); + + m_detailAabbPropertyIndex = terrainSrgLayout->FindShaderInputConstantIndex(AZ::Name(TerrainSrgInputs::DetailAabb)); + AZ_Error(TerrainFPName, m_detailAabbPropertyIndex.IsValid(), "Failed to find view srg input constant %s.", TerrainSrgInputs::DetailAabb); + + m_detailTexturesIndex = terrainSrgLayout->FindShaderInputImageUnboundedArrayIndex(AZ::Name(TerrainSrgInputs::DetailTextures)); + AZ_Error(TerrainFPName, m_detailTexturesIndex.IsValid(), "Failed to find view srg input constant %s.", TerrainSrgInputs::DetailTextures); + + // Set up the gpu buffer for detail material data + AZ::Render::GpuBufferHandler::Descriptor desc; + desc.m_bufferName = "Detail Material Data"; + desc.m_bufferSrgName = TerrainSrgInputs::DetailMaterialData; + desc.m_elementSize = sizeof(DetailMaterialShaderData); + desc.m_srgLayout = terrainSrgLayout; + m_detailMaterialDataBuffer = AZ::Render::GpuBufferHandler(desc); + } // Find any macro materials that have already been created. TerrainMacroMaterialRequestBus::EnumerateHandlers( @@ -987,7 +1142,7 @@ namespace Terrain auto objectSrg = AZ::RPI::ShaderResourceGroup::Create(shaderAsset, materialAsset->GetObjectSrgLayout()->GetName()); if (!objectSrg) { - AZ_Warning("TerrainFeatureProcessor", false, "Failed to create a new shader resource group, skipping."); + AZ_Warning(TerrainFPName, false, "Failed to create a new shader resource group, skipping."); continue; } @@ -1003,7 +1158,7 @@ namespace Terrain // set the shader option to select forward pass IBL specular if necessary if (!drawPacket.SetShaderOption(AZ::Name("o_meshUseForwardPassIBLSpecular"), AZ::RPI::ShaderOptionValue{ false })) { - AZ_Warning("MeshDrawPacket", false, "Failed to set o_meshUseForwardPassIBLSpecular on mesh draw packet"); + AZ_Warning(TerrainFPName, false, "Failed to set o_meshUseForwardPassIBLSpecular on mesh draw packet"); } const uint8_t stencilRef = AZ::Render::StencilRefs::UseDiffuseGIPass | AZ::Render::StencilRefs::UseIBLSpecularPass; drawPacket.SetStencilRef(stencilRef); @@ -1053,11 +1208,14 @@ namespace Terrain if (m_areaData.m_heightmapUpdated) { UpdateTerrainData(); - - const AZ::Data::Instance heightmapImage = m_areaData.m_heightmapImage; // cast StreamingImage to Image - m_materialInstance->SetPropertyValue(m_heightmapPropertyIndex, heightmapImage); } + if (m_updateDetailMaterialBuffer) + { + m_updateDetailMaterialBuffer = false; + m_detailMaterialDataBuffer.UpdateBuffer(m_detailMaterialShaderData.GetRawData(), aznumeric_cast(m_detailMaterialShaderData.GetSize())); + } + AZ::Vector3 cameraPosition = AZ::Vector3::CreateZero(); for (auto& view : process.m_views) { @@ -1068,7 +1226,7 @@ namespace Terrain } } - if (m_dirtyDetailRegion.IsValid() || !cameraPosition.IsClose(m_previousCameraPosition)) + if (m_dirtyDetailRegion.IsValid() || !cameraPosition.IsClose(m_previousCameraPosition) || m_detailImagesNeedUpdate) { int32_t newDetailTexturePosX = aznumeric_cast(AZStd::roundf(cameraPosition.GetX() / DetailTextureScale)); int32_t newDetailTexturePosY = aznumeric_cast(AZStd::roundf(cameraPosition.GetY() / DetailTextureScale)); @@ -1091,8 +1249,6 @@ namespace Terrain m_dirtyDetailRegion = AZ::Aabb::CreateNull(); m_previousCameraPosition = cameraPosition; - const AZ::Data::Instance detailTextureImage = m_detailTextureImage; // cast StreamingImage to Image - m_materialInstance->SetPropertyValue(m_detailMaterialIdPropertyIndex, detailTextureImage); AZ::Vector4 detailAabb = AZ::Vector4( m_detailTextureBounds.m_min.m_x * DetailTextureScale, @@ -1100,11 +1256,16 @@ namespace Terrain m_detailTextureBounds.m_max.m_x * DetailTextureScale, m_detailTextureBounds.m_max.m_y * DetailTextureScale ); - m_materialInstance->SetPropertyValue(m_detailAabbPropertyIndex, detailAabb); - m_materialInstance->SetPropertyValue(m_detailHalfPixelUvPropertyIndex, 0.5f / DetailTextureSize); - AZ::Vector2 detailUvOffset = AZ::Vector2(float(newCenter.m_x) / DetailTextureSize, float(newCenter.m_y) / DetailTextureSize); - m_materialInstance->SetPropertyValue(m_detailCenterPropertyIndex, detailUvOffset); + + if (m_terrainSrg) + { + m_terrainSrg->SetConstant(m_detailAabbPropertyIndex, detailAabb); + m_terrainSrg->SetConstant(m_detailHalfPixelUvPropertyIndex, 0.5f / DetailTextureSize); + m_terrainSrg->SetConstant(m_detailCenterPropertyIndex, detailUvOffset); + + m_detailMaterialDataBuffer.UpdateSrg(m_terrainSrg.get()); + } } if (m_areaData.m_heightmapUpdated || m_areaData.m_macroMaterialsUpdated) @@ -1195,6 +1356,15 @@ namespace Terrain sectorData.m_srg->Compile(); } } + + // Currently there seems to be a bug in unbounded image arrays where flickering can occur if this isn't updated every frame. + if (m_terrainSrg/* && m_detailImagesUpdated*/) + { + AZStd::array_view imageViews(m_detailImageViews.data(), m_detailImageViews.size()); + [[maybe_unused]] bool result = m_terrainSrg->SetImageViewUnboundedArray(m_detailTexturesIndex, imageViews); + AZ_Error(TerrainFPName, result, "Failed to set image view unbounded array into shader resource group."); + m_detailImagesNeedUpdate = false; + } } for (auto& sectorData : m_sectorData) @@ -1236,10 +1406,30 @@ namespace Terrain } } + if (m_detailTextureImage && m_areaData.m_heightmapImage && m_imagesNeedUpdate) + { + m_imagesNeedUpdate = false; + for (auto& view : process.m_views) + { + auto viewSrg = view->GetShaderResourceGroup(); + viewSrg->SetImage(m_heightmapPropertyIndex, m_areaData.m_heightmapImage); + } + if (m_terrainSrg) + { + m_terrainSrg->SetImage(m_detailMaterialIdPropertyIndex, m_detailTextureImage); + } + } + if (m_materialInstance) { m_materialInstance->Compile(); } + + if (m_terrainSrg && m_forwardPass) + { + m_terrainSrg->Compile(); + m_forwardPass->BindSrg(m_terrainSrg->GetRHIShaderResourceGroup()); + } } void TerrainFeatureProcessor::InitializeTerrainPatch(uint16_t gridSize, float gridSpacing, PatchData& patchdata) @@ -1368,6 +1558,7 @@ namespace Terrain void TerrainFeatureProcessor::OnMaterialReinitialized([[maybe_unused]] const MaterialInstance& material) { + PrepareMaterialData(); for (auto& sectorData : m_sectorData) { for (auto& drawPacket : sectorData.m_drawPackets) @@ -1375,6 +1566,8 @@ namespace Terrain drawPacket.Update(*GetParentScene()); } } + m_imagesNeedUpdate = true; + m_detailImagesNeedUpdate = true; } void TerrainFeatureProcessor::SetWorldSize([[maybe_unused]] AZ::Vector2 sizeInMeters) @@ -1438,6 +1631,27 @@ namespace Terrain } } } + + void TerrainFeatureProcessor::CacheForwardPass() + { + auto rasterPassFilter = AZ::RPI::PassFilter::CreateWithPassClass(); + rasterPassFilter.SetOwnerScene(GetParentScene()); + AZ::RHI::RHISystemInterface* rhiSystem = AZ::RHI::RHISystemInterface::Get(); + AZ::RHI::DrawListTag forwardTag = rhiSystem->GetDrawListTagRegistry()->AcquireTag(AZ::Name("forward")); + AZ::RPI::PassSystemInterface::Get()->ForEachPass(rasterPassFilter, + [&](AZ::RPI::Pass* pass) -> AZ::RPI::PassFilterExecutionFlow + { + auto* rasterPass = azrtti_cast(pass); + + if (rasterPass && rasterPass->GetDrawListTag() == forwardTag) + { + m_forwardPass = rasterPass; + return AZ::RPI::PassFilterExecutionFlow::StopVisitingPasses; + } + return AZ::RPI::PassFilterExecutionFlow::ContinueVisitingPasses; + } + ); + } auto TerrainFeatureProcessor::Vector2i::operator+(const Vector2i& rhs) const -> Vector2i { diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h index 9b66881ec6..7d68e6b185 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h @@ -19,7 +19,9 @@ #include #include #include +#include #include +#include namespace AZ::RPI { @@ -29,6 +31,7 @@ namespace AZ::RPI } class Material; class Model; + class RenderPass; class StreamingImage; } @@ -125,17 +128,19 @@ namespace Terrain UseTextureHeight = 0b0000'0000'0000'0000'0000'0000'0010'0000, UseTextureSpecularF0 = 0b0000'0000'0000'0000'0000'0000'0100'0000, - FlipNormalX = 0b0000'0000'0000'0000'0000'0000'1000'0000, - FlipNormalY = 0b0000'0000'0000'0000'0000'0001'0000'0000, + FlipNormalX = 0b0000'0000'0000'0001'0000'0000'0000'0000, + FlipNormalY = 0b0000'0000'0000'0010'0000'0000'0000'0000, - BlendModeMask = 0b0000'0000'0000'0000'0000'0110'0000'0000, + BlendModeMask = 0b0000'0000'0000'1100'0000'0000'0000'0000, BlendModeLerp = 0b0000'0000'0000'0000'0000'0000'0000'0000, - BlendModeLinearLight = 0b0000'0000'0000'0000'0000'0010'0000'0000, - BlendModeMultiply = 0b0000'0000'0000'0000'0000'0100'0000'0000, - BlendModeOverlay = 0b0000'0000'0000'0000'0000'0110'0000'0000, + BlendModeLinearLight = 0b0000'0000'0000'0100'0000'0000'0000'0000, + BlendModeMultiply = 0b0000'0000'0000'1000'0000'0000'0000'0000, + BlendModeOverlay = 0b0000'0000'0000'1100'0000'0000'0000'0000, }; - struct DetailMaterialShaderProperties + static constexpr uint16_t InvalidDetailImageIndex = 0xFFFF; + + struct DetailMaterialShaderData { // Uv AZStd::array m_uvTransform @@ -145,30 +150,50 @@ namespace Terrain 0.0, 0.0, 1.0, 0.0, }; + float m_baseColorRed{ 1.0f }; + float m_baseColorGreen{ 1.0f }; + float m_baseColorBlue{ 1.0f }; + // Factor / Scale / Bias for input textures float m_baseColorFactor{ 1.0f }; + float m_normalFactor{ 1.0f }; float m_metalFactor{ 1.0f }; float m_roughnessScale{ 1.0f }; - float m_roughnessBias{ 0.0f }; + float m_specularF0Factor{ 1.0f }; float m_occlusionFactor{ 1.0f }; float m_heightFactor{ 1.0f }; - float m_heightOffset{ 0.0f }; + float m_heightBlendFactor{ 0.5f }; // Flags DetailTextureFlags m_flags{ 0 }; - float m_padding; // 16 byte aligned + // Image indices + uint16_t m_colorImageIndex{ InvalidDetailImageIndex }; + uint16_t m_normalImageIndex{ InvalidDetailImageIndex }; + uint16_t m_roughnessImageIndex{ InvalidDetailImageIndex }; + uint16_t m_metalnessImageIndex{ InvalidDetailImageIndex }; + + uint16_t m_specularF0ImageIndex{ InvalidDetailImageIndex }; + uint16_t m_occlusionImageIndex{ InvalidDetailImageIndex }; + uint16_t m_heightImageIndex{ InvalidDetailImageIndex }; + + // 16 byte aligned + uint16_t m_padding1; + uint32_t m_padding2; + uint32_t m_padding3; }; struct DetailMaterialData { AZ::Data::AssetId m_assetId; AZ::RPI::Material::ChangeId m_materialChangeId{AZ::RPI::Material::DEFAULT_CHANGE_ID}; + uint32_t refCount = 0; + uint16_t m_detailMaterialBufferIndex{ 0xFFFF }; AZ::Data::Instance m_colorImage; AZ::Data::Instance m_normalImage; @@ -177,8 +202,6 @@ namespace Terrain AZ::Data::Instance m_specularF0Image; AZ::Data::Instance m_occlusionImage; AZ::Data::Instance m_heightImage; - - DetailMaterialShaderProperties m_properties; // maps directly to shader }; struct DetailMaterialSurface @@ -217,6 +240,12 @@ namespace Terrain Aabb2i GetClamped(Aabb2i rhs) const; bool IsValid() const; }; + + struct DetailTextureLocation + { + uint16_t m_index; + AZ::Data::Instance m_image; + }; // AZ::RPI::MaterialReloadNotificationBus::Handler overrides... void OnMaterialReinitialized(const MaterialInstance& material) override; @@ -237,6 +266,9 @@ namespace Terrain void OnTerrainSurfaceMaterialMappingChanged(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override; void OnTerrainSurfaceMaterialMappingRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override; + // AZ::RPI::SceneNotificationBus overrides... + void OnRenderPipelinePassesChanged(AZ::RPI::RenderPipeline* renderPipeline) override; + void Initialize(); void InitializeTerrainPatch(uint16_t gridSize, float gridSpacing, PatchData& patchdata); bool InitializePatchModel(); @@ -249,7 +281,8 @@ namespace Terrain void TerrainSurfaceDataUpdated(const AZ::Aabb& dirtyRegion); uint16_t CreateOrUpdateDetailMaterial(MaterialInstance material); - void UpdateDetailMaterialData(DetailMaterialData& materialData, MaterialInstance material); + void CheckDetailMaterialForDeletion(uint16_t detailMaterialId); + void UpdateDetailMaterialData(uint16_t detailMaterialIndex, MaterialInstance material); void CheckUpdateDetailTexture(const Aabb2i& newBounds, const Vector2i& newCenter); void UpdateDetailTexture(const Aabb2i& updateArea, const Aabb2i& textureBounds, const Vector2i& centerPixel); uint16_t GetDetailMaterialForSurfaceTypeAndPosition(AZ::Crc32 surfaceType, const AZ::Vector2& position); @@ -271,6 +304,8 @@ namespace Terrain AZ::Outcome> CreateBufferAsset( const void* data, const AZ::RHI::BufferViewDescriptor& bufferViewDescriptor, const AZStd::string& bufferName); + void CacheForwardPass(); + // System-level parameters static constexpr float GridSpacing{ 1.0f }; static constexpr int32_t GridSize{ 64 }; // number of terrain quads (vertices are m_gridSize + 1) @@ -281,6 +316,7 @@ namespace Terrain AZStd::unique_ptr m_materialAssetLoader; MaterialInstance m_materialInstance; + AZ::Data::Instance m_terrainSrg; AZ::RHI::ShaderInputConstantIndex m_modelToWorldIndex; AZ::RHI::ShaderInputConstantIndex m_terrainDataIndex; @@ -288,11 +324,13 @@ namespace Terrain AZ::RHI::ShaderInputConstantIndex m_macroMaterialCountIndex; AZ::RHI::ShaderInputImageIndex m_macroColorMapIndex; AZ::RHI::ShaderInputImageIndex m_macroNormalMapIndex; - AZ::RPI::MaterialPropertyIndex m_heightmapPropertyIndex; - AZ::RPI::MaterialPropertyIndex m_detailMaterialIdPropertyIndex; - AZ::RPI::MaterialPropertyIndex m_detailCenterPropertyIndex; - AZ::RPI::MaterialPropertyIndex m_detailAabbPropertyIndex; - AZ::RPI::MaterialPropertyIndex m_detailHalfPixelUvPropertyIndex; + AZ::RHI::ShaderInputImageIndex m_heightmapPropertyIndex; + AZ::RHI::ShaderInputImageIndex m_detailMaterialIdPropertyIndex; + AZ::RHI::ShaderInputBufferIndex m_detailMaterialDataIndex; + AZ::RHI::ShaderInputConstantIndex m_detailCenterPropertyIndex; + AZ::RHI::ShaderInputConstantIndex m_detailAabbPropertyIndex; + AZ::RHI::ShaderInputConstantIndex m_detailHalfPixelUvPropertyIndex; + AZ::RHI::ShaderInputImageUnboundedArrayIndex m_detailTexturesIndex; AZ::Data::Instance m_patchModel; AZ::Vector3 m_previousCameraPosition = AZ::Vector3(AZStd::numeric_limits::max(), 0.0, 0.0); @@ -312,17 +350,26 @@ namespace Terrain TerrainAreaData m_areaData; AZ::Aabb m_dirtyRegion{ AZ::Aabb::CreateNull() }; AZ::Aabb m_dirtyDetailRegion{ AZ::Aabb::CreateNull() }; + bool m_updateDetailMaterialBuffer{ false }; Aabb2i m_detailTextureBounds; Vector2i m_detailTextureCenter; AZ::Data::Instance m_detailTextureImage; AZ::RPI::ShaderSystemInterface::GlobalShaderOptionUpdatedEvent::Handler m_handleGlobalShaderOptionUpdate; - bool m_forceRebuildDrawPackets = false; + bool m_forceRebuildDrawPackets{ false }; + bool m_imagesNeedUpdate{ false }; AZStd::vector m_sectorData; AZ::Render::IndexedDataVector m_macroMaterials; AZ::Render::IndexedDataVector m_detailMaterials; AZ::Render::IndexedDataVector m_detailMaterialRegions; + AZ::Render::SparseVector m_detailMaterialShaderData; + AZ::Render::GpuBufferHandler m_detailMaterialDataBuffer; + AZ::RPI::RenderPass* m_forwardPass; + + AZStd::vector m_detailImageViews; + AZStd::vector m_detailImageViewFreeList; + bool m_detailImagesNeedUpdate{ false }; }; }