diff --git a/Assets/Editor/Icons/Components/NonUniformScale.svg b/Assets/Editor/Icons/Components/NonUniformScale.svg new file mode 100644 index 0000000000..f377232d62 --- /dev/null +++ b/Assets/Editor/Icons/Components/NonUniformScale.svg @@ -0,0 +1,27 @@ + + + Icons / Toolbar / Non Uniform Scaling + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AutomatedTesting/Gem/Code/runtime_dependencies.cmake b/AutomatedTesting/Gem/Code/runtime_dependencies.cmake index c8e66740e4..62a6ed7f8c 100644 --- a/AutomatedTesting/Gem/Code/runtime_dependencies.cmake +++ b/AutomatedTesting/Gem/Code/runtime_dependencies.cmake @@ -53,5 +53,6 @@ set(GEM_DEPENDENCIES Gem::ImguiAtom Gem::Atom_AtomBridge Gem::AtomFont + Gem::NvCloth Gem::Blast ) diff --git a/AutomatedTesting/Gem/Code/tool_dependencies.cmake b/AutomatedTesting/Gem/Code/tool_dependencies.cmake index 8c5da63f42..a6bbeee350 100644 --- a/AutomatedTesting/Gem/Code/tool_dependencies.cmake +++ b/AutomatedTesting/Gem/Code/tool_dependencies.cmake @@ -68,5 +68,6 @@ set(GEM_DEPENDENCIES Gem::ImguiAtom Gem::AtomFont Gem::AtomToolsFramework.Editor + Gem::NvCloth.Editor Gem::Blast.Editor ) diff --git a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt index 31afab87ed..3124f1048a 100644 --- a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt @@ -107,20 +107,21 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) endif() ## NvCloth ## -# [TODO LYN-1928] Enable when AutomatedTesting runs with Atom -#if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) -# ly_add_pytest( -# NAME AutomatedTesting::NvClothTests -# TEST_SUITE main -# TEST_SERIAL -# PATH ${CMAKE_CURRENT_LIST_DIR}/NvCloth/TestSuite_Active.py -# TIMEOUT 1500 -# RUNTIME_DEPENDENCIES -# Legacy::Editor -# AZ::AssetProcessor -# AutomatedTesting.Assets -# ) -#endif() +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) + ly_add_pytest( + NAME AutomatedTesting::NvClothTests_Main + TEST_SUITE main + TEST_SERIAL + PATH ${CMAKE_CURRENT_LIST_DIR}/NvCloth/TestSuite_Active.py + TIMEOUT 1500 + RUNTIME_DEPENDENCIES + Legacy::Editor + AZ::AssetProcessor + AutomatedTesting.Assets + COMPONENT + NvCloth + ) +endif() ## Editor Python Bindings ## if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) diff --git a/AutomatedTesting/Gem/PythonTests/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh.py b/AutomatedTesting/Gem/PythonTests/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh.py index 2677ba5605..625c9772bd 100755 --- a/AutomatedTesting/Gem/PythonTests/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh.py +++ b/AutomatedTesting/Gem/PythonTests/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh.py @@ -20,7 +20,7 @@ class Tests: exit_game_mode = ("Exited game mode", "Failed to exit game mode") # fmt: on -def run(): +def C18977329_NvCloth_AddClothSimulationToMesh(): """ Summary: Load level with Entity having Mesh and Cloth components already setup. Verify that editor remains stable in Game mode. @@ -89,4 +89,7 @@ def run(): helper.close_editor() if __name__ == "__main__": - run() + import ImportPathHelper as imports + imports.init() + from editor_python_test_tools.utils import Report + Report.start_test(C18977329_NvCloth_AddClothSimulationToMesh) diff --git a/AutomatedTesting/Gem/PythonTests/NvCloth/C18977330_NvCloth_AddClothSimulationToActor.py b/AutomatedTesting/Gem/PythonTests/NvCloth/C18977330_NvCloth_AddClothSimulationToActor.py index 2d4fa4e325..9b3135cd2b 100755 --- a/AutomatedTesting/Gem/PythonTests/NvCloth/C18977330_NvCloth_AddClothSimulationToActor.py +++ b/AutomatedTesting/Gem/PythonTests/NvCloth/C18977330_NvCloth_AddClothSimulationToActor.py @@ -20,7 +20,7 @@ class Tests: exit_game_mode = ("Exited game mode", "Failed to exit game mode") # fmt: on -def run(): +def C18977330_NvCloth_AddClothSimulationToActor(): """ Summary: Load level with Entity having Actor and Cloth components already setup. Verify that editor remains stable in Game mode. @@ -89,4 +89,7 @@ def run(): helper.close_editor() if __name__ == "__main__": - run() + import ImportPathHelper as imports + imports.init() + from editor_python_test_tools.utils import Report + Report.start_test(C18977330_NvCloth_AddClothSimulationToActor) diff --git a/AutomatedTesting/Gem/PythonTests/NvCloth/TestSuite_Active.py b/AutomatedTesting/Gem/PythonTests/NvCloth/TestSuite_Active.py index 162c54afc8..86bfc48636 100755 --- a/AutomatedTesting/Gem/PythonTests/NvCloth/TestSuite_Active.py +++ b/AutomatedTesting/Gem/PythonTests/NvCloth/TestSuite_Active.py @@ -21,14 +21,15 @@ sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesti from base import TestAutomationBase -@pytest.mark.SUITE_main @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) class TestAutomation(TestAutomationBase): + @pytest.mark.xfail(reason="Running with atom null renderer is causing this test to fail") def test_C18977329_NvCloth_AddClothSimulationToMesh(self, request, workspace, editor, launcher_platform): from . import C18977329_NvCloth_AddClothSimulationToMesh as test_module self._run_test(request, workspace, editor, test_module) + @pytest.mark.xfail(reason="Running with atom null renderer is causing this test to fail") def test_C18977330_NvCloth_AddClothSimulationToActor(self, request, workspace, editor, launcher_platform): from . import C18977330_NvCloth_AddClothSimulationToActor as test_module self._run_test(request, workspace, editor, test_module) diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py b/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py index aafa2ff9b7..f84e367d1d 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py +++ b/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py @@ -27,6 +27,155 @@ TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "atom_hydra_scripts") @pytest.mark.parametrize("level", ["auto_test"]) class TestAtomEditorComponentsMain(object): - # It requires at least one test - def test_Dummy(self, request, editor, level, workspace, project, launcher_platform): - pass + @pytest.mark.test_case_id( + "C32078130", # Display Mapper + "C32078129", # Light + "C32078131", # Radius Weight Modifier + "C32078127", # PostFX Layer + "C32078125", # Physical Sky + "C32078115", # Global Skylight (IBL) + "C32078121", # Exposure Control + "C32078120", # Directional Light + "C32078119", # DepthOfField + "C32078118") # Decal (Atom) + def test_AtomEditorComponents_AddedToEntity(self, request, editor, level, workspace, project, launcher_platform): + cfg_args = [level] + + expected_lines = [ + # Decal (Atom) Component + "Decal (Atom) Entity successfully created", + "Decal (Atom)_test: Component added to the entity: True", + "Decal (Atom)_test: Component removed after UNDO: True", + "Decal (Atom)_test: Component added after REDO: True", + "Decal (Atom)_test: Entered game mode: True", + "Decal (Atom)_test: Exit game mode: True", + "Decal (Atom) Controller|Configuration|Material: SUCCESS", + "Decal (Atom)_test: Entity is hidden: True", + "Decal (Atom)_test: Entity is shown: True", + "Decal (Atom)_test: Entity deleted: True", + "Decal (Atom)_test: UNDO entity deletion works: True", + "Decal (Atom)_test: REDO entity deletion works: True", + # DepthOfField Component + "DepthOfField Entity successfully created", + "DepthOfField_test: Component added to the entity: True", + "DepthOfField_test: Component removed after UNDO: True", + "DepthOfField_test: Component added after REDO: True", + "DepthOfField_test: Entered game mode: True", + "DepthOfField_test: Exit game mode: True", + "DepthOfField_test: Entity disabled initially: True", + "DepthOfField_test: Entity enabled after adding required components: True", + "DepthOfField Controller|Configuration|Camera Entity: SUCCESS", + "DepthOfField_test: Entity is hidden: True", + "DepthOfField_test: Entity is shown: True", + "DepthOfField_test: Entity deleted: True", + "DepthOfField_test: UNDO entity deletion works: True", + "DepthOfField_test: REDO entity deletion works: True", + # Exposure Control Component + "Exposure Control Entity successfully created", + "Exposure Control_test: Component added to the entity: True", + "Exposure Control_test: Component removed after UNDO: True", + "Exposure Control_test: Component added after REDO: True", + "Exposure Control_test: Entered game mode: True", + "Exposure Control_test: Exit game mode: True", + "Exposure Control_test: Entity disabled initially: True", + "Exposure Control_test: Entity enabled after adding required components: True", + "Exposure Control_test: Entity is hidden: True", + "Exposure Control_test: Entity is shown: True", + "Exposure Control_test: Entity deleted: True", + "Exposure Control_test: UNDO entity deletion works: True", + "Exposure Control_test: REDO entity deletion works: True", + # Global Skylight (IBL) Component + "Global Skylight (IBL) Entity successfully created", + "Global Skylight (IBL)_test: Component added to the entity: True", + "Global Skylight (IBL)_test: Component removed after UNDO: True", + "Global Skylight (IBL)_test: Component added after REDO: True", + "Global Skylight (IBL)_test: Entered game mode: True", + "Global Skylight (IBL)_test: Exit game mode: True", + "Global Skylight (IBL) Controller|Configuration|Diffuse Image: SUCCESS", + "Global Skylight (IBL) Controller|Configuration|Specular Image: SUCCESS", + "Global Skylight (IBL)_test: Entity is hidden: True", + "Global Skylight (IBL)_test: Entity is shown: True", + "Global Skylight (IBL)_test: Entity deleted: True", + "Global Skylight (IBL)_test: UNDO entity deletion works: True", + "Global Skylight (IBL)_test: REDO entity deletion works: True", + # Physical Sky Component + "Physical Sky Entity successfully created", + "Physical Sky component was added to entity", + "Entity has a Physical Sky component", + "Physical Sky_test: Component added to the entity: True", + "Physical Sky_test: Component removed after UNDO: True", + "Physical Sky_test: Component added after REDO: True", + "Physical Sky_test: Entered game mode: True", + "Physical Sky_test: Exit game mode: True", + "Physical Sky_test: Entity is hidden: True", + "Physical Sky_test: Entity is shown: True", + "Physical Sky_test: Entity deleted: True", + "Physical Sky_test: UNDO entity deletion works: True", + "Physical Sky_test: REDO entity deletion works: True", + # PostFX Layer Component + "PostFX Layer Entity successfully created", + "PostFX Layer_test: Component added to the entity: True", + "PostFX Layer_test: Component removed after UNDO: True", + "PostFX Layer_test: Component added after REDO: True", + "PostFX Layer_test: Entered game mode: True", + "PostFX Layer_test: Exit game mode: True", + "PostFX Layer_test: Entity is hidden: True", + "PostFX Layer_test: Entity is shown: True", + "PostFX Layer_test: Entity deleted: True", + "PostFX Layer_test: UNDO entity deletion works: True", + "PostFX Layer_test: REDO entity deletion works: True", + # Radius Weight Modifier Component + "Radius Weight Modifier Entity successfully created", + "Radius Weight Modifier_test: Component added to the entity: True", + "Radius Weight Modifier_test: Component removed after UNDO: True", + "Radius Weight Modifier_test: Component added after REDO: True", + "Radius Weight Modifier_test: Entered game mode: True", + "Radius Weight Modifier_test: Exit game mode: True", + "Radius Weight Modifier_test: Entity is hidden: True", + "Radius Weight Modifier_test: Entity is shown: True", + "Radius Weight Modifier_test: Entity deleted: True", + "Radius Weight Modifier_test: UNDO entity deletion works: True", + "Radius Weight Modifier_test: REDO entity deletion works: True", + # Light Component + "Light Entity successfully created", + "Light_test: Component added to the entity: True", + "Light_test: Component removed after UNDO: True", + "Light_test: Component added after REDO: True", + "Light_test: Entered game mode: True", + "Light_test: Exit game mode: True", + "Light_test: Entity is hidden: True", + "Light_test: Entity is shown: True", + "Light_test: Entity deleted: True", + "Light_test: UNDO entity deletion works: True", + "Light_test: REDO entity deletion works: True", + # Display Mapper Component + "Display Mapper Entity successfully created", + "Display Mapper_test: Component added to the entity: True", + "Display Mapper_test: Component removed after UNDO: True", + "Display Mapper_test: Component added after REDO: True", + "Display Mapper_test: Entered game mode: True", + "Display Mapper_test: Exit game mode: True", + "Display Mapper_test: Entity is hidden: True", + "Display Mapper_test: Entity is shown: True", + "Display Mapper_test: Entity deleted: True", + "Display Mapper_test: UNDO entity deletion works: True", + "Display Mapper_test: REDO entity deletion works: True", + ] + + unexpected_lines = [ + "failed to open", + "Traceback (most recent call last):", + ] + + hydra.launch_and_validate_results( + request, + TEST_DIRECTORY, + editor, + "hydra_AtomEditorComponents_AddedToEntity.py", + timeout=EDITOR_TIMEOUT, + expected_lines=expected_lines, + unexpected_lines=unexpected_lines, + halt_on_unexpected=True, + null_renderer=True, + cfg_args=cfg_args, + ) diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_SandboxSuite.py b/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_SandboxSuite.py index 8ca5b5aa31..0fb873e677 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_SandboxSuite.py +++ b/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_SandboxSuite.py @@ -19,170 +19,6 @@ import pytest @pytest.mark.parametrize("level", ["auto_test"]) class TestAtomEditorComponentsSandbox(object): - @pytest.mark.test_case_id( - "C32078117", # Area Light - "C32078130", # Display Mapper - "C32078129", # Light - "C32078131", # Radius Weight Modifier - "C32078127", # PostFX Layer - "C32078126", # Point Light - "C32078125", # Physical Sky - "C32078115", # Global Skylight (IBL) - "C32078121", # Exposure Control - "C32078120", # Directional Light - "C32078119", # DepthOfField - "C32078118") # Decal - def test_AtomEditorComponents_AddedToEntity(self, request, editor, level, workspace, project, launcher_platform): - cfg_args = [level] - - expected_lines = [ - # Decal Component - "Decal (Atom) Entity successfully created", - "Decal (Atom)_test: Component added to the entity: True", - "Decal (Atom)_test: Component removed after UNDO: True", - "Decal (Atom)_test: Component added after REDO: True", - "Decal (Atom)_test: Entered game mode: True", - "Decal (Atom)_test: Exit game mode: True", - "Decal (Atom) Controller|Configuration|Material: SUCCESS", - "Decal (Atom)_test: Entity is hidden: True", - "Decal (Atom)_test: Entity is shown: True", - "Decal (Atom)_test: Entity deleted: True", - "Decal (Atom)_test: UNDO entity deletion works: True", - "Decal (Atom)_test: REDO entity deletion works: True", - # DepthOfField Component - "DepthOfField Entity successfully created", - "DepthOfField_test: Component added to the entity: True", - "DepthOfField_test: Component removed after UNDO: True", - "DepthOfField_test: Component added after REDO: True", - "DepthOfField_test: Entered game mode: True", - "DepthOfField_test: Exit game mode: True", - "DepthOfField_test: Entity disabled initially: True", - "DepthOfField_test: Entity enabled after adding required components: True", - "DepthOfField Controller|Configuration|Camera Entity: SUCCESS", - "DepthOfField_test: Entity is hidden: True", - "DepthOfField_test: Entity is shown: True", - "DepthOfField_test: Entity deleted: True", - "DepthOfField_test: UNDO entity deletion works: True", - "DepthOfField_test: REDO entity deletion works: True", - # Directional Light Component - "Directional Light Entity successfully created", - "Directional Light_test: Component added to the entity: True", - "Directional Light_test: Component removed after UNDO: True", - "Directional Light_test: Component added after REDO: True", - "Directional Light_test: Entered game mode: True", - "Directional Light_test: Exit game mode: True", - "Directional Light Controller|Configuration|Shadow|Camera: SUCCESS", - "Directional Light_test: Entity is hidden: True", - "Directional Light_test: Entity is shown: True", - "Directional Light_test: Entity deleted: True", - "Directional Light_test: UNDO entity deletion works: True", - "Directional Light_test: REDO entity deletion works: True", - # Exposure Control Component - "Exposure Control Entity successfully created", - "Exposure Control_test: Component added to the entity: True", - "Exposure Control_test: Component removed after UNDO: True", - "Exposure Control_test: Component added after REDO: True", - "Exposure Control_test: Entered game mode: True", - "Exposure Control_test: Exit game mode: True", - "Exposure Control_test: Entity disabled initially: True", - "Exposure Control_test: Entity enabled after adding required components: True", - "Exposure Control_test: Entity is hidden: True", - "Exposure Control_test: Entity is shown: True", - "Exposure Control_test: Entity deleted: True", - "Exposure Control_test: UNDO entity deletion works: True", - "Exposure Control_test: REDO entity deletion works: True", - # Global Skylight (IBL) Component - "Global Skylight (IBL) Entity successfully created", - "Global Skylight (IBL)_test: Component added to the entity: True", - "Global Skylight (IBL)_test: Component removed after UNDO: True", - "Global Skylight (IBL)_test: Component added after REDO: True", - "Global Skylight (IBL)_test: Entered game mode: True", - "Global Skylight (IBL)_test: Exit game mode: True", - "Global Skylight (IBL) Controller|Configuration|Diffuse Image: SUCCESS", - "Global Skylight (IBL) Controller|Configuration|Specular Image: SUCCESS", - "Global Skylight (IBL)_test: Entity is hidden: True", - "Global Skylight (IBL)_test: Entity is shown: True", - "Global Skylight (IBL)_test: Entity deleted: True", - "Global Skylight (IBL)_test: UNDO entity deletion works: True", - "Global Skylight (IBL)_test: REDO entity deletion works: True", - # Physical Sky Component - "Physical Sky Entity successfully created", - "Physical Sky component was added to entity", - "Entity has a Physical Sky component", - "Physical Sky_test: Component added to the entity: True", - "Physical Sky_test: Component removed after UNDO: True", - "Physical Sky_test: Component added after REDO: True", - "Physical Sky_test: Entered game mode: True", - "Physical Sky_test: Exit game mode: True", - "Physical Sky_test: Entity is hidden: True", - "Physical Sky_test: Entity is shown: True", - "Physical Sky_test: Entity deleted: True", - "Physical Sky_test: UNDO entity deletion works: True", - "Physical Sky_test: REDO entity deletion works: True", - # PostFX Layer Component - "PostFX Layer Entity successfully created", - "PostFX Layer_test: Component added to the entity: True", - "PostFX Layer_test: Component removed after UNDO: True", - "PostFX Layer_test: Component added after REDO: True", - "PostFX Layer_test: Entered game mode: True", - "PostFX Layer_test: Exit game mode: True", - "PostFX Layer_test: Entity is hidden: True", - "PostFX Layer_test: Entity is shown: True", - "PostFX Layer_test: Entity deleted: True", - "PostFX Layer_test: UNDO entity deletion works: True", - "PostFX Layer_test: REDO entity deletion works: True", - # Radius Weight Modifier Component - "Radius Weight Modifier Entity successfully created", - "Radius Weight Modifier_test: Component added to the entity: True", - "Radius Weight Modifier_test: Component removed after UNDO: True", - "Radius Weight Modifier_test: Component added after REDO: True", - "Radius Weight Modifier_test: Entered game mode: True", - "Radius Weight Modifier_test: Exit game mode: True", - "Radius Weight Modifier_test: Entity is hidden: True", - "Radius Weight Modifier_test: Entity is shown: True", - "Radius Weight Modifier_test: Entity deleted: True", - "Radius Weight Modifier_test: UNDO entity deletion works: True", - "Radius Weight Modifier_test: REDO entity deletion works: True", - # Light Component - "Light Entity successfully created", - "Light_test: Component added to the entity: True", - "Light_test: Component removed after UNDO: True", - "Light_test: Component added after REDO: True", - "Light_test: Entered game mode: True", - "Light_test: Exit game mode: True", - "Light_test: Entity is hidden: True", - "Light_test: Entity is shown: True", - "Light_test: Entity deleted: True", - "Light_test: UNDO entity deletion works: True", - "Light_test: REDO entity deletion works: True", - # Display Mapper Component - "Display Mapper Entity successfully created", - "Display Mapper_test: Component added to the entity: True", - "Display Mapper_test: Component removed after UNDO: True", - "Display Mapper_test: Component added after REDO: True", - "Display Mapper_test: Entered game mode: True", - "Display Mapper_test: Exit game mode: True", - "Display Mapper_test: Entity is hidden: True", - "Display Mapper_test: Entity is shown: True", - "Display Mapper_test: Entity deleted: True", - "Display Mapper_test: UNDO entity deletion works: True", - "Display Mapper_test: REDO entity deletion works: True", - ] - - unexpected_lines = [ - "failed to open", - "Traceback (most recent call last):", - ] - - hydra.launch_and_validate_results( - request, - TEST_DIRECTORY, - editor, - "hydra_AtomEditorComponents_AddedToEntity.py", - timeout=EDITOR_TIMEOUT, - expected_lines=expected_lines, - unexpected_lines=unexpected_lines, - halt_on_unexpected=True, - null_renderer=True, - cfg_args=cfg_args, - ) + # It requires at least one test + def test_Dummy(self, request, editor, level, workspace, project, launcher_platform): + pass diff --git a/AutomatedTesting/Gem/PythonTests/editor/test_AssetPicker.py b/AutomatedTesting/Gem/PythonTests/editor/test_AssetPicker.py index ac499d734f..90ca3690e4 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/test_AssetPicker.py +++ b/AutomatedTesting/Gem/PythonTests/editor/test_AssetPicker.py @@ -42,6 +42,7 @@ class TestAssetPicker(object): @pytest.mark.test_case_id("C13751579", "C1508814") @pytest.mark.SUITE_periodic + @pytest.mark.xfail # ATOM-15493 def test_AssetPicker_UI_UX(self, request, editor, level, launcher_platform): expected_lines = [ "TestEntity Entity successfully created", diff --git a/AutomatedTesting/Gem/PythonTests/physics/C14861501_PhysXCollider_RenderMeshAutoAssigned.py b/AutomatedTesting/Gem/PythonTests/physics/C14861501_PhysXCollider_RenderMeshAutoAssigned.py index e415687001..bc7b1b5e00 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C14861501_PhysXCollider_RenderMeshAutoAssigned.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C14861501_PhysXCollider_RenderMeshAutoAssigned.py @@ -98,5 +98,5 @@ if __name__ == "__main__": import ImportPathHelper as imports imports.init() - from utils import Report + from editor_python_test_tools.utils import Report Report.start_test(C14861501_PhysXCollider_RenderMeshAutoAssigned) diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4044695_PhysXCollider_AddMultipleSurfaceFbx.py b/AutomatedTesting/Gem/PythonTests/physics/C4044695_PhysXCollider_AddMultipleSurfaceFbx.py index d65e9050bd..57cdc8f9c5 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4044695_PhysXCollider_AddMultipleSurfaceFbx.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4044695_PhysXCollider_AddMultipleSurfaceFbx.py @@ -114,5 +114,5 @@ if __name__ == "__main__": import ImportPathHelper as imports imports.init() - from utils import Report + from editor_python_test_tools.utils import Report Report.start_test(C4044695_PhysXCollider_AddMultipleSurfaceFbx) diff --git a/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Periodic.py index 3bbfe64e38..2b82c3e596 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Periodic.py +++ b/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Periodic.py @@ -429,6 +429,8 @@ class TestAutomation(TestAutomationBase): from . import C4976236_AddPhysxColliderComponent as test_module self._run_test(request, workspace, editor, test_module) + @pytest.mark.xfail( + reason="This will fail due to this issue ATOM-15487.") def test_C14861502_PhysXCollider_AssetAutoAssigned(self, request, workspace, editor, launcher_platform): from . import C14861502_PhysXCollider_AssetAutoAssigned as test_module self._run_test(request, workspace, editor, test_module) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvents_ReturnSetType_Successfully.py b/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvents_ReturnSetType_Successfully.py new file mode 100644 index 0000000000..aa9abd110b --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvents_ReturnSetType_Successfully.py @@ -0,0 +1,112 @@ +""" +All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +its licensors. + +For complete copyright and license terms please see the LICENSE at the root of this +distribution (the "License"). All use of this software is governed by the License, +or, if provided, by the license below or the license accompanying this file. Do not +remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +Test Case Title: Event can return a value of set type successfully +""" + + +# fmt: off +class Tests(): + level_created = ("Successfully created temporary level", "Failed to create temporary level") + entity_created = ("Successfully created test entity", "Failed to create test entity") + enter_game_mode = ("Successfully entered game mode", "Failed to enter game mode") + lines_found = ("Successfully found expected message", "Failed to find expected message") + exit_game_mode = ("Successfully exited game mode", "Failed to exit game mode") +# fmt: on + + +def ScriptEvents_ReturnSetType_Successfully(): + """ + Summary: A temporary level is created with an Entity having ScriptCanvas component. + ScriptEvent(T92569006_ScriptEvent.scriptevents) is created with one Method that has a return value. + ScriptCanvas(T92569006_ScriptCanvas.scriptcanvas) is attached to Entity. Graph has Send node that sends the Method + of the ScriptEvent and prints the returned result ( On Entity Activated -> Send node -> Print) and Receive node is + set to return custom value ( Receive node -> Print). + Verify that the entity containing T92569006_ScriptCanvas.scriptcanvas should print the custom value set in both + Send and Receive nodes. + + Expected Behavior: + After entering game mode, the graph on the entity should print an expected message to the console + + Test Steps: + 1) Create test level + 2) Create test entity + 3) Start Tracer + 4) Enter Game Mode + 5) Read for line + 6) Exit Game Mode + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ + import os + from editor_entity_utils import EditorEntity as Entity + from utils import Report + from utils import TestHelper as helper + from utils import Tracer + + import azlmbr.legacy.general as general + import azlmbr.asset as asset + import azlmbr.math as math + import azlmbr.bus as bus + + LEVEL_NAME = "tmp_level" + WAIT_TIME = 3.0 # SECONDS + EXPECTED_LINES = ["T92569006_ScriptEvent_Sent", "T92569006_ScriptEvent_Received"] + SC_ASSET_PATH = os.path.join("ScriptCanvas", "T92569006_ScriptCanvas.scriptcanvas") + + def create_editor_entity(name, sc_asset): + entity = Entity.create_editor_entity(name) + sc_comp = entity.add_component("Script Canvas") + asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", sc_asset, math.Uuid(), False) + sc_comp.set_component_property_value("Script Canvas Asset|Script Canvas Asset", asset_id) + Report.critical_result(Tests.entity_created, entity.id.isValid()) + + def locate_expected_lines(line_list: list): + found_lines = [printInfo.message.strip() for printInfo in section_tracer.prints] + + return all(line in found_lines for line in line_list) + + # 1) Create temp level + general.idle_enable(True) + result = general.create_level_no_prompt(LEVEL_NAME, 128, 1, 512, True) + Report.critical_result(Tests.level_created, result == 0) + helper.wait_for_condition(lambda: general.get_current_level_name() == LEVEL_NAME, WAIT_TIME) + general.close_pane("Error Report") + + # 2) Create test entity + create_editor_entity("TestEntity", SC_ASSET_PATH) + + # 3) Start Tracer + with Tracer() as section_tracer: + + # 4) Enter Game Mode + helper.enter_game_mode(Tests.enter_game_mode) + + # 5) Read for line + lines_located = helper.wait_for_condition(lambda: locate_expected_lines(EXPECTED_LINES), WAIT_TIME) + Report.result(Tests.lines_found, lines_located) + + # 6) Exit Game Mode + helper.exit_game_mode(Tests.exit_game_mode) + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + + from utils import Report + + Report.start_test(ScriptEvents_ReturnSetType_Successfully) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Active.py b/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Active.py index a47596c7b5..192e11d378 100755 --- a/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Active.py +++ b/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Active.py @@ -183,6 +183,15 @@ class TestAutomation(TestAutomationBase): from . import ScriptEvents_SendReceiveSuccessfully as test_module self._run_test(request, workspace, editor, test_module) + @pytest.mark.parametrize("level", ["tmp_level"]) + def test_ScriptEvents_ReturnSetType_Successfully(self, request, workspace, editor, launcher_platform, project, level): + def teardown(): + file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True) + request.addfinalizer(teardown) + file_system.delete([os.path.join(workspace.paths.project(), "Levels", level)], True, True) + from . import ScriptEvents_ReturnSetType_Successfully as test_module + self._run_test(request, workspace, editor, test_module) + def test_NodeCategory_ExpandOnClick(self, request, workspace, editor, launcher_platform): from . import NodeCategory_ExpandOnClick as test_module self._run_test(request, workspace, editor, test_module) diff --git a/AutomatedTesting/Levels/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh/C18977329_NvCloth_AddClothSimulationToMesh.ly b/AutomatedTesting/Levels/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh/C18977329_NvCloth_AddClothSimulationToMesh.ly index 17fee158d8..1afbf787db 100644 --- a/AutomatedTesting/Levels/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh/C18977329_NvCloth_AddClothSimulationToMesh.ly +++ b/AutomatedTesting/Levels/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh/C18977329_NvCloth_AddClothSimulationToMesh.ly @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2a3360287a4711882c4254d64ca2ba70cd743012a7d38ca29aa2a57f151efaa -size 6661 +oid sha256:e15d484113e8151072b410924747a8ad304f6f12457fad577308c0491693ab34 +size 5472 diff --git a/AutomatedTesting/Levels/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh/filelist.xml b/AutomatedTesting/Levels/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh/filelist.xml index 6c8b361e57..9775a35c53 100644 --- a/AutomatedTesting/Levels/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh/filelist.xml +++ b/AutomatedTesting/Levels/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh/filelist.xml @@ -1,6 +1,6 @@ - + diff --git a/AutomatedTesting/Levels/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh/level.pak b/AutomatedTesting/Levels/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh/level.pak index e80d5ca1d9..08a775b6c8 100644 --- a/AutomatedTesting/Levels/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh/level.pak +++ b/AutomatedTesting/Levels/NvCloth/C18977329_NvCloth_AddClothSimulationToMesh/level.pak @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd8105f020151e65093988dfb09ab42ff8d33ef5b97c61fbe0011384870aadf8 -size 39238 +oid sha256:64de37c805b0be77cdb7a85b5406af58b7f845e7d97fec1721ac5d789bb641db +size 38856 diff --git a/AutomatedTesting/Levels/NvCloth/C18977330_NvCloth_AddClothSimulationToActor/C18977330_NvCloth_AddClothSimulationToActor.ly b/AutomatedTesting/Levels/NvCloth/C18977330_NvCloth_AddClothSimulationToActor/C18977330_NvCloth_AddClothSimulationToActor.ly index 031989ee11..385027c479 100644 --- a/AutomatedTesting/Levels/NvCloth/C18977330_NvCloth_AddClothSimulationToActor/C18977330_NvCloth_AddClothSimulationToActor.ly +++ b/AutomatedTesting/Levels/NvCloth/C18977330_NvCloth_AddClothSimulationToActor/C18977330_NvCloth_AddClothSimulationToActor.ly @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f53fb5e096ff562e9f0f12856ce387891596776d086f49c7ed3a59dcd0a0c11a -size 6535 +oid sha256:7b595323d4d51211463dea0338abb6ce2a4a0a8d41efb12ac3c9dccd1f972171 +size 5504 diff --git a/AutomatedTesting/Levels/NvCloth/C18977330_NvCloth_AddClothSimulationToActor/filelist.xml b/AutomatedTesting/Levels/NvCloth/C18977330_NvCloth_AddClothSimulationToActor/filelist.xml index 290a28f223..7ccc1d51eb 100644 --- a/AutomatedTesting/Levels/NvCloth/C18977330_NvCloth_AddClothSimulationToActor/filelist.xml +++ b/AutomatedTesting/Levels/NvCloth/C18977330_NvCloth_AddClothSimulationToActor/filelist.xml @@ -1,6 +1,6 @@ - + diff --git a/AutomatedTesting/Levels/NvCloth/C18977330_NvCloth_AddClothSimulationToActor/level.pak b/AutomatedTesting/Levels/NvCloth/C18977330_NvCloth_AddClothSimulationToActor/level.pak index fb91adeba5..12ce03fa87 100644 --- a/AutomatedTesting/Levels/NvCloth/C18977330_NvCloth_AddClothSimulationToActor/level.pak +++ b/AutomatedTesting/Levels/NvCloth/C18977330_NvCloth_AddClothSimulationToActor/level.pak @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:87fbd9fda267daa505f11276b64f47c26115bee9e6d14f2a6f5a1cf1e1234218 -size 39179 +oid sha256:617c455668fc41cb7fd69de690e4aa3c80f2cb36deaa371902b79de18fcd1cb2 +size 39233 diff --git a/AutomatedTesting/ScriptCanvas/T92569006_ScriptCanvas.scriptcanvas b/AutomatedTesting/ScriptCanvas/T92569006_ScriptCanvas.scriptcanvas new file mode 100644 index 0000000000..626ee29d96 --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/T92569006_ScriptCanvas.scriptcanvas @@ -0,0 +1,1952 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutomatedTesting/TestAssets/T92569006.scriptevents b/AutomatedTesting/TestAssets/T92569006.scriptevents new file mode 100644 index 0000000000..518b1b5733 --- /dev/null +++ b/AutomatedTesting/TestAssets/T92569006.scriptevents @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Code/CryEngine/CryCommon/WinBase.cpp b/Code/CryEngine/CryCommon/WinBase.cpp index d48a3327a7..e6ea1cd4a8 100644 --- a/Code/CryEngine/CryCommon/WinBase.cpp +++ b/Code/CryEngine/CryCommon/WinBase.cpp @@ -77,7 +77,7 @@ unsigned int g_EnableMultipleAssert = 0;//set to something else than 0 if to ena #endif #if defined(APPLE) - #include "../CrySystem/SystemUtilsApple.h" + #include #endif #include "StringUtils.h" diff --git a/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp b/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp index d31f122d9b..8818dfa0d9 100644 --- a/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp +++ b/Code/CryEngine/CrySystem/LevelSystem/LevelSystem.cpp @@ -108,10 +108,11 @@ bool CLevelInfo::ReadInfo() AzFramework::ApplicationRequests::Bus::BroadcastResult( usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled); + // Set up a default game type for legacy code. + m_defaultGameTypeName = "Mission0"; + if (usePrefabSystemForLevels) { - // Set up a default game type for legacy code. - m_defaultGameTypeName = "Mission0"; return true; } diff --git a/Code/CryEngine/CrySystem/Log.cpp b/Code/CryEngine/CrySystem/Log.cpp index cdf145e4be..62cf29fef9 100644 --- a/Code/CryEngine/CrySystem/Log.cpp +++ b/Code/CryEngine/CrySystem/Log.cpp @@ -51,7 +51,7 @@ #define LOG_BACKUP_PATH "@log@/LogBackups" #if defined(IOS) -#include "SystemUtilsApple.h" +#include #endif ////////////////////////////////////////////////////////////////////// diff --git a/Code/CryEngine/CrySystem/MobileDetectSpec_Ios.cpp b/Code/CryEngine/CrySystem/MobileDetectSpec_Ios.cpp index 1f3275f1c6..dc0a28490d 100644 --- a/Code/CryEngine/CrySystem/MobileDetectSpec_Ios.cpp +++ b/Code/CryEngine/CrySystem/MobileDetectSpec_Ios.cpp @@ -15,7 +15,7 @@ #include #include "MobileDetectSpec.h" -#include "SystemUtilsApple.h" +#include namespace MobileSysInspect { diff --git a/Code/CryEngine/CrySystem/Platform/Mac/platform_mac_files.cmake b/Code/CryEngine/CrySystem/Platform/Mac/platform_mac_files.cmake index 9c26988e94..4d5680a30d 100644 --- a/Code/CryEngine/CrySystem/Platform/Mac/platform_mac_files.cmake +++ b/Code/CryEngine/CrySystem/Platform/Mac/platform_mac_files.cmake @@ -8,8 +8,3 @@ # remove or modify any license notices. This file is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # - -set(FILES - ../../SystemUtilsApple.h - ../../SystemUtilsApple.mm -) diff --git a/Code/CryEngine/CrySystem/Platform/iOS/platform_ios_files.cmake b/Code/CryEngine/CrySystem/Platform/iOS/platform_ios_files.cmake index a5d743e6d7..bbe61fb488 100644 --- a/Code/CryEngine/CrySystem/Platform/iOS/platform_ios_files.cmake +++ b/Code/CryEngine/CrySystem/Platform/iOS/platform_ios_files.cmake @@ -13,8 +13,6 @@ set(FILES ../../MobileDetectSpec_Ios.cpp ../../MobileDetectSpec.cpp ../../MobileDetectSpec.h - ../../SystemUtilsApple.h - ../../SystemUtilsApple.mm ) diff --git a/Code/CryEngine/CrySystem/SystemWin32.cpp b/Code/CryEngine/CrySystem/SystemWin32.cpp index ce352966ac..b47519eb03 100644 --- a/Code/CryEngine/CrySystem/SystemWin32.cpp +++ b/Code/CryEngine/CrySystem/SystemWin32.cpp @@ -66,7 +66,7 @@ __pragma(comment(lib, "Winmm.lib")) #endif #if defined(APPLE) -#include "SystemUtilsApple.h" +#include #endif diff --git a/Code/CryEngine/CrySystem/crysystem_mac_files.cmake b/Code/CryEngine/CrySystem/crysystem_mac_files.cmake index 7e539e6825..f5b9ea77a2 100644 --- a/Code/CryEngine/CrySystem/crysystem_mac_files.cmake +++ b/Code/CryEngine/CrySystem/crysystem_mac_files.cmake @@ -9,7 +9,3 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -set(FILES - SystemUtilsApple.h - SystemUtilsApple.mm -) diff --git a/Code/Framework/AzCore/AzCore/Math/MathMatrixSerializer.cpp b/Code/Framework/AzCore/AzCore/Math/MathMatrixSerializer.cpp new file mode 100644 index 0000000000..0b7e3300cf --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Math/MathMatrixSerializer.cpp @@ -0,0 +1,485 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AZ::JsonMathMatrixSerializerInternal +{ + template + JsonSerializationResult::Result LoadArray(MatrixType& output, const rapidjson::Value& inputValue, JsonDeserializerContext& context) + { + namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds. + + constexpr size_t ElementCount = RowCount * ColumnCount; + static_assert(ElementCount == 9 || ElementCount == 12 || ElementCount == 16, + "MathMatrixSerializer only support Matrix3x3, Matrix3x4 and Matrix4x4."); + + rapidjson::SizeType arraySize = inputValue.Size(); + if (arraySize < ElementCount) + { + return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported, + "Not enough numbers in JSON array to load math matrix from."); + } + + AZ::BaseJsonSerializer* floatSerializer = context.GetRegistrationContext()->GetSerializerForType(azrtti_typeid()); + if (!floatSerializer) + { + return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Catastrophic, "Failed to find the JSON float serializer."); + } + + constexpr const char* names[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"}; + float values[ElementCount]; + for (int i = 0; i < ElementCount; ++i) + { + ScopedContextPath subPath(context, names[i]); + JSR::Result intermediate = floatSerializer->Load(values + i, azrtti_typeid(), inputValue[i], context); + if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed) + { + return intermediate; + } + } + + size_t valueIndex = 0; + for (size_t r = 0; r < RowCount; ++r) + { + for (size_t c = 0; c < ColumnCount; ++c) + { + output.SetElement(aznumeric_caster(r), aznumeric_caster(c), values[valueIndex++]); + } + } + + return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success, "Successfully read math matrix."); + } + + JsonSerializationResult::Result LoadFloatFromObject( + float& output, + const rapidjson::Value& inputValue, + JsonDeserializerContext& context, + const char* name, + const char* altName) + { + namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds. + + AZ::BaseJsonSerializer* floatSerializer = context.GetRegistrationContext()->GetSerializerForType(azrtti_typeid()); + if (!floatSerializer) + { + return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Catastrophic, "Failed to find the json float serializer."); + } + + const char* nameUsed = name; + JSR::ResultCode result(JSR::Tasks::ReadField); + auto iterator = inputValue.FindMember(rapidjson::StringRef(name)); + if (iterator == inputValue.MemberEnd()) + { + nameUsed = altName; + iterator = inputValue.FindMember(rapidjson::StringRef(altName)); + if (iterator == inputValue.MemberEnd()) + { + // field not found so leave default value + result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed)); + nameUsed = nullptr; + } + } + + if (nameUsed) + { + ScopedContextPath subPath(context, nameUsed); + JSR::Result intermediate = floatSerializer->Load(&output, azrtti_typeid(), iterator->value, context); + if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed) + { + return intermediate; + } + else + { + result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Success)); + } + } + + return context.Report(result, "Successfully read float."); + } + + JsonSerializationResult::Result LoadVector3FromObject( + Vector3& output, + const rapidjson::Value& inputValue, + JsonDeserializerContext& context, + AZStd::fixed_vector names) + { + namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds. + constexpr size_t ElementCount = 3; // Vector3 + + JSR::ResultCode result(JSR::Tasks::ReadField); + float values[ElementCount]; + for (int i = 0; i < ElementCount; ++i) + { + values[i] = output.GetElement(i); + auto name = names[i * 2]; + auto altName = names[(i * 2) + 1]; + + JSR::Result intermediate = LoadFloatFromObject(values[i], inputValue, context, name.data(), altName.data()); + if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed) + { + return intermediate; + } + else + { + result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Success)); + } + } + + for (int i = 0; i < ElementCount; ++i) + { + output.SetElement(i, values[i]); + } + + return context.Report(result, "Successfully read math matrix."); + } + + JsonSerializationResult::Result LoadQuaternionAndScale( + AZ::Quaternion& quaternion, + float& scale, + const rapidjson::Value& inputValue, + JsonDeserializerContext& context) + { + namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds. + + JSR::ResultCode result(JSR::Tasks::ReadField); + scale = 1.0f; + JSR::Result intermediateScale = LoadFloatFromObject(scale, inputValue, context, "scale", "Scale"); + if (intermediateScale.GetResultCode().GetProcessing() != JSR::Processing::Completed) + { + return intermediateScale; + } + result.Combine(intermediateScale); + + if (AZ::IsClose(scale, 0.0f)) + { + result.Combine({ JSR::Tasks::ReadField, JSR::Outcomes::Unsupported }); + return context.Report(result, "Scale can not be zero."); + } + + AZ::Vector3 degreesRollPitchYaw = AZ::Vector3::CreateZero(); + JSR::Result intermediateDegrees = LoadVector3FromObject(degreesRollPitchYaw, inputValue, context, { "roll", "Roll", "pitch", "Pitch", "yaw", "Yaw" }); + if (intermediateDegrees.GetResultCode().GetProcessing() != JSR::Processing::Completed) + { + return intermediateDegrees; + } + result.Combine(intermediateDegrees); + + // the quaternion should be equivalent to a series of rotations in the order z, then y, then x + const AZ::Vector3 eulerRadians = AZ::Vector3DegToRad(degreesRollPitchYaw); + quaternion = AZ::Quaternion::CreateRotationX(eulerRadians.GetX()) * + AZ::Quaternion::CreateRotationY(eulerRadians.GetY()) * + AZ::Quaternion::CreateRotationZ(eulerRadians.GetZ()); + + return context.Report(result, "Successfully read math yaw, pitch, roll, and scale."); + } + + template + JsonSerializationResult::Result LoadObject(MatrixType& output, const rapidjson::Value& inputValue, JsonDeserializerContext& context) + { + namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds. + output = MatrixType::CreateIdentity(); + + JSR::ResultCode result(JSR::Tasks::ReadField); + float scale; + AZ::Quaternion rotation; + + JSR::Result intermediate = LoadQuaternionAndScale(rotation, scale, inputValue, context); + if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed) + { + return intermediate; + } + result.Combine(intermediate); + + AZ::Vector3 translation = AZ::Vector3::CreateZero(); + JSR::Result intermediateTranslation = LoadVector3FromObject(translation, inputValue, context, { "x", "X", "y", "Y", "z", "Z" }); + if (intermediateTranslation.GetResultCode().GetProcessing() != JSR::Processing::Completed) + { + return intermediateTranslation; + } + result.Combine(intermediateTranslation); + + // composed a matrix by rotation, then scale, then translation + auto matrix = MatrixType::CreateFromQuaternion(rotation); + matrix.MultiplyByScale(Vector3{ scale }); + matrix.SetTranslation(translation); + + if (matrix == MatrixType::CreateIdentity()) + { + return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Using identity matrix for empty object."); + } + + output = matrix; + return context.Report(result, "Successfully read math matrix."); + } + + template<> + JsonSerializationResult::Result LoadObject(Matrix3x3& output, const rapidjson::Value& inputValue, JsonDeserializerContext& context) + { + namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds. + output = Matrix3x3::CreateIdentity(); + + JSR::ResultCode result(JSR::Tasks::ReadField); + float scale; + AZ::Quaternion rotation; + + JSR::Result intermediate = LoadQuaternionAndScale(rotation, scale, inputValue, context); + if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed) + { + return intermediate; + } + result.Combine(intermediate); + + // composed a matrix by rotation then scale + auto matrix = Matrix3x3::CreateFromQuaternion(rotation); + matrix.MultiplyByScale(Vector3{ scale }); + + if (matrix == Matrix3x3::CreateIdentity()) + { + return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Using identity matrix for empty object."); + } + + output = matrix; + return context.Report(result, "Successfully read math matrix."); + } + + template + JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, + const rapidjson::Value& inputValue, JsonDeserializerContext& context) + { + namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds. + + constexpr size_t ElementCount = RowCount * ColumnCount; + static_assert(ElementCount == 9 || ElementCount == 12 || ElementCount == 16, + "MathMatrixSerializer only support Matrix3x3, Matrix3x4 and Matrix4x4."); + + AZ_Assert(azrtti_typeid() == outputValueTypeId, + "Unable to deserialize Matrix%zux%zu to json because the provided type is %s", + RowCount, ColumnCount, outputValueTypeId.ToString().c_str()); + AZ_UNUSED(outputValueTypeId); + + MatrixType* matrix = reinterpret_cast(outputValue); + AZ_Assert(matrix, "Output value for JsonMatrix%zux%zuSerializer can't be null.", RowCount, ColumnCount); + + switch (inputValue.GetType()) + { + case rapidjson::kArrayType: + return LoadArray(*matrix, inputValue, context); + case rapidjson::kObjectType: + return LoadObject(*matrix, inputValue, context); + + case rapidjson::kStringType: + [[fallthrough]]; + case rapidjson::kNumberType: + [[fallthrough]]; + case rapidjson::kNullType: + [[fallthrough]]; + case rapidjson::kFalseType: + [[fallthrough]]; + case rapidjson::kTrueType: + return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported, + "Unsupported type. Math matrix can only be read from arrays or objects."); + + default: + return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown, + "Unknown json type encountered in math matrix."); + } + } + + template + AZ::Quaternion CreateQuaternion(const MatrixType& matrix); + + template<> + AZ::Quaternion CreateQuaternion(const AZ::Matrix3x3& matrix) + { + return Quaternion::CreateFromMatrix3x3(matrix); + } + + template<> + AZ::Quaternion CreateQuaternion(const AZ::Matrix3x4& matrix) + { + return Quaternion::CreateFromMatrix3x4(matrix); + } + + template<> + AZ::Quaternion CreateQuaternion(const AZ::Matrix4x4& matrix) + { + return Quaternion::CreateFromMatrix4x4(matrix); + } + + template + JsonSerializationResult::Result StoreRotationAndScale(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, + const Uuid& valueTypeId, JsonSerializerContext& context) + { + namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds. + AZ_UNUSED(valueTypeId); + + const MatrixType* matrix = reinterpret_cast(inputValue); + AZ_Assert(matrix, "Input value for JsonMatrixSerializer can't be null."); + const MatrixType* defaultMatrix = reinterpret_cast(defaultValue); + + if (!context.ShouldKeepDefaults() && defaultMatrix && *matrix == *defaultMatrix) + { + return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "Default math Matrix used."); + } + + MatrixType matrixToExport = *matrix; + AZ::Vector3 scale = matrixToExport.ExtractScale(); + + AZ::Quaternion rotation = CreateQuaternion(matrixToExport); + auto degrees = rotation.GetEulerDegrees(); + outputValue.AddMember(rapidjson::StringRef("roll"), degrees.GetX(), context.GetJsonAllocator()); + outputValue.AddMember(rapidjson::StringRef("pitch"), degrees.GetY(), context.GetJsonAllocator()); + outputValue.AddMember(rapidjson::StringRef("yaw"), degrees.GetZ(), context.GetJsonAllocator()); + outputValue.AddMember(rapidjson::StringRef("scale"), scale.GetX(), context.GetJsonAllocator()); + + return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::Success, "Math Matrix successfully stored."); + } + + template + JsonSerializationResult::Result StoreTranslation(rapidjson::Value& outputValue, const void* inputValue, + const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) + { + namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds. + AZ_UNUSED(valueTypeId); + + const MatrixType* matrix = reinterpret_cast(inputValue); + AZ_Assert(matrix, "Input value for JsonMatrixSerializer can't be null."); + const MatrixType* defaultMatrix = reinterpret_cast(defaultValue); + + if (!context.ShouldKeepDefaults() && defaultMatrix && *matrix == *defaultMatrix) + { + return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "Default math Matrix used."); + } + + auto translation = matrix->GetTranslation(); + outputValue.AddMember(rapidjson::StringRef("x"), translation.GetX(), context.GetJsonAllocator()); + outputValue.AddMember(rapidjson::StringRef("y"), translation.GetY(), context.GetJsonAllocator()); + outputValue.AddMember(rapidjson::StringRef("z"), translation.GetZ(), context.GetJsonAllocator()); + + return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::Success, "Math Matrix successfully stored."); + } +} + +namespace AZ +{ + // Matrix3x3 + + AZ_CLASS_ALLOCATOR_IMPL(JsonMatrix3x3Serializer, SystemAllocator, 0); + + JsonSerializationResult::Result JsonMatrix3x3Serializer::Load(void* outputValue, const Uuid& outputValueTypeId, + const rapidjson::Value& inputValue, JsonDeserializerContext& context) + { + return JsonMathMatrixSerializerInternal::Load( + outputValue, + outputValueTypeId, + inputValue, + context); + } + + JsonSerializationResult::Result JsonMatrix3x3Serializer::Store(rapidjson::Value& outputValue, const void* inputValue, + const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) + { + outputValue.SetObject(); + + return JsonMathMatrixSerializerInternal::StoreRotationAndScale( + outputValue, + inputValue, + defaultValue, + valueTypeId, + context); + } + + + // Matrix3x4 + + AZ_CLASS_ALLOCATOR_IMPL(JsonMatrix3x4Serializer, SystemAllocator, 0); + + JsonSerializationResult::Result JsonMatrix3x4Serializer::Load(void* outputValue, const Uuid& outputValueTypeId, + const rapidjson::Value& inputValue, JsonDeserializerContext& context) + { + return JsonMathMatrixSerializerInternal::Load( + outputValue, + outputValueTypeId, + inputValue, + context); + } + + JsonSerializationResult::Result JsonMatrix3x4Serializer::Store(rapidjson::Value& outputValue, const void* inputValue, + const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) + { + outputValue.SetObject(); + + auto result = JsonMathMatrixSerializerInternal::StoreRotationAndScale( + outputValue, + inputValue, + defaultValue, + valueTypeId, + context); + + auto resultTranslation = JsonMathMatrixSerializerInternal::StoreTranslation( + outputValue, + inputValue, + defaultValue, + valueTypeId, + context); + + result.GetResultCode().Combine(resultTranslation); + return result; + } + + // Matrix4x4 + + AZ_CLASS_ALLOCATOR_IMPL(JsonMatrix4x4Serializer, SystemAllocator, 0); + + JsonSerializationResult::Result JsonMatrix4x4Serializer::Load(void* outputValue, const Uuid& outputValueTypeId, + const rapidjson::Value& inputValue, JsonDeserializerContext& context) + { + return JsonMathMatrixSerializerInternal::Load( + outputValue, + outputValueTypeId, + inputValue, + context); + } + + JsonSerializationResult::Result JsonMatrix4x4Serializer::Store(rapidjson::Value& outputValue, const void* inputValue, + const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) + { + outputValue.SetObject(); + + auto result = JsonMathMatrixSerializerInternal::StoreRotationAndScale( + outputValue, + inputValue, + defaultValue, + valueTypeId, + context); + + auto resultTranslation = JsonMathMatrixSerializerInternal::StoreTranslation( + outputValue, + inputValue, + defaultValue, + valueTypeId, + context); + + result.GetResultCode().Combine(resultTranslation); + return result; + } +} diff --git a/Code/Framework/AzCore/AzCore/Math/MathMatrixSerializer.h b/Code/Framework/AzCore/AzCore/Math/MathMatrixSerializer.h new file mode 100644 index 0000000000..81c9635a79 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Math/MathMatrixSerializer.h @@ -0,0 +1,54 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include + +namespace AZ +{ + class JsonMatrix3x3Serializer + : public BaseJsonSerializer + { + public: + AZ_RTTI(JsonMatrix3x3Serializer, "{8C76CD6A-8576-4604-A746-CF7A7F20F366}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR_DECL; + JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue, + JsonDeserializerContext& context) override; + JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, + const Uuid& valueTypeId, JsonSerializerContext& context) override; + }; + + class JsonMatrix3x4Serializer + : public BaseJsonSerializer + { + public: + AZ_RTTI(JsonMatrix3x4Serializer, "{E801333B-4AF1-4F43-976C-579670B02DC5}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR_DECL; + JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue, + JsonDeserializerContext& context) override; + JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, + const Uuid& valueTypeId, JsonSerializerContext& context) override; + }; + + class JsonMatrix4x4Serializer + : public BaseJsonSerializer + { + public: + AZ_RTTI(JsonMatrix4x4Serializer, "{46E888FC-248A-4910-9221-4E101A10AEA1}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR_DECL; + JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue, + JsonDeserializerContext& context) override; + JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, + const Uuid& valueTypeId, JsonSerializerContext& context) override; + }; +} diff --git a/Code/Framework/AzCore/AzCore/Math/MathReflection.cpp b/Code/Framework/AzCore/AzCore/Math/MathReflection.cpp index e918f8fdb3..3c683f1988 100644 --- a/Code/Framework/AzCore/AzCore/Math/MathReflection.cpp +++ b/Code/Framework/AzCore/AzCore/Math/MathReflection.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -366,6 +367,9 @@ namespace AZ { context.Serializer()->HandlesType(); context.Serializer()->HandlesType(); + context.Serializer()->HandlesType(); + context.Serializer()->HandlesType(); + context.Serializer()->HandlesType(); context.Serializer()->HandlesType(); context.Serializer()->HandlesType(); context.Serializer()->HandlesType(); diff --git a/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl b/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl index 90b9ba5afd..1016027966 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl +++ b/Code/Framework/AzCore/AzCore/Serialization/EditContextConstants.inl @@ -53,6 +53,10 @@ namespace AZ //! RemoveableByUser : A bool which determines if the component can be removed by the user. //! Setting this to false prevents the user from removing this component. Default behavior is removeable by user. const static AZ::Crc32 RemoveableByUser = AZ_CRC("RemoveableByUser", 0x32c7fd50); + //! An int which, if specified, causes a component to be forced to a particular position in the sorted list of + //! components on an entity, and prevents dragging or moving operations which would affect that position. + const static AZ::Crc32 FixedComponentListIndex = AZ_CRC_CE("FixedComponentListIndex"); + const static AZ::Crc32 AppearsInAddComponentMenu = AZ_CRC("AppearsInAddComponentMenu", 0x53790e31); const static AZ::Crc32 ForceAutoExpand = AZ_CRC("ForceAutoExpand", 0x1a5c79d2); // Ignores expansion state set by user, enforces expansion. const static AZ::Crc32 AutoExpand = AZ_CRC("AutoExpand", 0x306ff5c0); // Expands automatically unless user changes expansion state. diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/ArraySerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/ArraySerializer.cpp index 13a25e5aa6..d5a1730364 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/ArraySerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/ArraySerializer.cpp @@ -74,7 +74,7 @@ namespace AZ "Unable to retrieve the correct container information for AZStd::array instance."); } - Flags flags = Flags::None; + ContinuationFlags flags = ContinuationFlags::None; Uuid elementTypeId = Uuid::CreateNull(); auto typeEnumCallback = [&elementTypeId, &flags](const Uuid&, const SerializeContext::ClassElement* genericClassElement) { @@ -82,7 +82,7 @@ namespace AZ elementTypeId = genericClassElement->m_typeId; if (genericClassElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER) { - flags = Flags::ResolvePointer; + flags = ContinuationFlags::ResolvePointer; } return false; }; @@ -161,7 +161,7 @@ namespace AZ "Not enough entries in JSON array to load an AZStd::array from."); } - Flags flags = Flags::None; + ContinuationFlags flags = ContinuationFlags::None; Uuid elementTypeId = Uuid::CreateNull(); auto typeEnumCallback = [&elementTypeId, &flags](const Uuid&, const SerializeContext::ClassElement* genericClassElement) { @@ -169,7 +169,7 @@ namespace AZ elementTypeId = genericClassElement->m_typeId; if (genericClassElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER) { - flags = Flags::ResolvePointer; + flags = ContinuationFlags::ResolvePointer; } return false; }; diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.cpp index 6c67fd284a..9a426a1e59 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.cpp @@ -208,22 +208,28 @@ namespace AZ // BaseJsonSerializer // - JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoading(void* object, const Uuid& typeId, const rapidjson::Value& value, - JsonDeserializerContext& context, Flags flags) + BaseJsonSerializer::OperationFlags BaseJsonSerializer::GetOperationsFlags() const { - return flags & Flags::ResolvePointer ? - JsonDeserializer::LoadToPointer(object, typeId, value, context) : - JsonDeserializer::Load(object, typeId, value, context); + return OperationFlags::None; } - JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoring(rapidjson::Value& output, const void* object, - const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context, Flags flags) + JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoading( + void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context, ContinuationFlags flags) + { + return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer + ? JsonDeserializer::LoadToPointer(object, typeId, value, context) + : JsonDeserializer::Load(object, typeId, value, context); + } + + JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoring( + rapidjson::Value& output, const void* object, const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context, + ContinuationFlags flags) { using namespace JsonSerializationResult; - if (flags & Flags::ReplaceDefault && !context.ShouldKeepDefaults()) + if ((flags & ContinuationFlags::ReplaceDefault) == ContinuationFlags::ReplaceDefault && !context.ShouldKeepDefaults()) { - if (flags & Flags::ResolvePointer) + if ((flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer) { return JsonSerializer::StoreFromPointer(output, object, nullptr, typeId, context); } @@ -248,7 +254,7 @@ namespace AZ } } - return flags & Flags::ResolvePointer ? + return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer ? JsonSerializer::StoreFromPointer(output, object, defaultObject, typeId, context) : JsonSerializer::Store(output, object, defaultObject, typeId, context); } @@ -265,8 +271,9 @@ namespace AZ return JsonSerializer::StoreTypeName(output, typeId, context); } - JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoadingFromJsonObjectField(void* object, const Uuid& typeId, const rapidjson::Value& value, - rapidjson::Value::StringRefType memberName, JsonDeserializerContext& context, Flags flags) + JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoadingFromJsonObjectField( + void* object, const Uuid& typeId, const rapidjson::Value& value, rapidjson::Value::StringRefType memberName, + JsonDeserializerContext& context, ContinuationFlags flags) { using namespace JsonSerializationResult; @@ -291,7 +298,7 @@ namespace AZ JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoringToJsonObjectField(rapidjson::Value& output, rapidjson::Value::StringRefType newMemberName, const void* object, const void* defaultObject, - const Uuid& typeId, JsonSerializerContext& context, Flags flags) + const Uuid& typeId, JsonSerializerContext& context, ContinuationFlags flags) { using namespace JsonSerializationResult; diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.h b/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.h index f6ced44583..06c5eda6de 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/BaseJsonSerializer.h @@ -161,13 +161,19 @@ namespace AZ public: AZ_RTTI(BaseJsonSerializer, "{7291FFDC-D339-40B5-BB26-EA067A327B21}"); - enum Flags + enum class ContinuationFlags { - None = 0, //! No extra flags. + None = 0, //! No extra flags. ResolvePointer = 1 << 0, //! The pointer passed in contains a pointer. The (de)serializer will attempt to resolve to an instance. ReplaceDefault = 1 << 1 //! The default value provided for storing will be replaced with a newly created one. }; + enum class OperationFlags + { + None = 0, //! No flags that control how the custom json serializer is used. + ManualDefault = 1 << 0 //! Even if an (explicit) default is found the custom json serializer will still be called. + }; + virtual ~BaseJsonSerializer() = default; //! Transforms the data from the rapidjson Value to outputValue, if the conversion is possible and supported. @@ -180,6 +186,9 @@ namespace AZ virtual JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) = 0; + //! Returns the operation flags which tells the Json Serialization how this custom json serializer can be used. + virtual OperationFlags GetOperationsFlags() const; + protected: //! Continues loading of a (sub)value. Use this function to load member variables for instance. This is more optimal than //! directly calling the json serialization. @@ -187,8 +196,9 @@ namespace AZ //! @param typeId Type id of the object passed in. //! @param value The value in the JSON document where the deserializer will start reading data from. //! @param context The context used during deserialization. Use the value passed in from Load. - JsonSerializationResult::ResultCode ContinueLoading(void* object, const Uuid& typeId, const rapidjson::Value& value, - JsonDeserializerContext& context, Flags flags = Flags::None); + JsonSerializationResult::ResultCode ContinueLoading( + void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context, + ContinuationFlags flags = ContinuationFlags::None); //! Continues storing of a (sub)value. Use this function to store member variables for instance. This is more optimal than //! directly calling the json serialization. @@ -200,8 +210,9 @@ namespace AZ //! the settings. //! @param typeId The type id of the object and default object. //! @param context The context used during serialization. Use the value passed in from Store. - JsonSerializationResult::ResultCode ContinueStoring(rapidjson::Value& output, const void* object, const void* defaultObject, - const Uuid& typeId, JsonSerializerContext& context, Flags flags = Flags::None); + JsonSerializationResult::ResultCode ContinueStoring( + rapidjson::Value& output, const void* object, const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context, + ContinuationFlags flags = ContinuationFlags::None); //! Retrieves the type id from a json object or json string. //! @param typeId The retrieved type id. @@ -222,12 +233,14 @@ namespace AZ const Uuid& typeId, JsonSerializerContext& context); //! Helper function similar to ContinueLoading, but loads the data as a member of 'value' rather than 'value' itself, if it exists. - JsonSerializationResult::ResultCode ContinueLoadingFromJsonObjectField(void* object, const Uuid& typeId, const rapidjson::Value& value, - rapidjson::Value::StringRefType memberName, JsonDeserializerContext& context, Flags flags = Flags::None); + JsonSerializationResult::ResultCode ContinueLoadingFromJsonObjectField( + void* object, const Uuid& typeId, const rapidjson::Value& value, rapidjson::Value::StringRefType memberName, + JsonDeserializerContext& context, ContinuationFlags flags = ContinuationFlags::None); //! Helper function similar to ContinueStoring, but stores the data as a member of 'output' rather than overwriting 'output'. - JsonSerializationResult::ResultCode ContinueStoringToJsonObjectField(rapidjson::Value& output, rapidjson::Value::StringRefType newMemberName, - const void* object, const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context, Flags flags = Flags::None); + JsonSerializationResult::ResultCode ContinueStoringToJsonObjectField( + rapidjson::Value& output, rapidjson::Value::StringRefType newMemberName, const void* object, const void* defaultObject, + const Uuid& typeId, JsonSerializerContext& context, ContinuationFlags flags = ContinuationFlags::None); //! Checks if a value is an explicit default. This useful for containers where not storing anything as a default would mean //! a slot wouldn't be used so something has to be added to represent the fully default target. @@ -238,6 +251,7 @@ namespace AZ rapidjson::Value GetExplicitDefault(); }; - AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::BaseJsonSerializer::Flags) + AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::BaseJsonSerializer::ContinuationFlags) + AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::BaseJsonSerializer::OperationFlags) } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/BasicContainerSerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/BasicContainerSerializer.cpp index c15cb9ef54..400a3b7949 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/BasicContainerSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/BasicContainerSerializer.cpp @@ -75,9 +75,10 @@ namespace AZ auto elementCallback = [this, &array, &retVal, &index, &context] (void* elementPtr, const Uuid& elementId, const SerializeContext::ClassData*, const SerializeContext::ClassElement* classElement) { - Flags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ? - Flags::ResolvePointer : Flags::None; - flags |= Flags::ReplaceDefault; + ContinuationFlags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER + ? ContinuationFlags::ResolvePointer + : ContinuationFlags::None; + flags |= ContinuationFlags::ReplaceDefault; ScopedContextPath subPath(context, index); index++; @@ -161,8 +162,9 @@ namespace AZ container->EnumTypes(typeEnumCallback); AZ_Assert(classElement, "No class element found for the type in the basic container."); - Flags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ? - Flags::ResolvePointer : Flags::None; + ContinuationFlags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER + ? ContinuationFlags::ResolvePointer + : ContinuationFlags::None; const size_t capacity = container->IsFixedCapacity() ? container->Capacity(outputValue) : std::numeric_limits::max(); diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp index 8d0da9e54a..93d12acba3 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.cpp @@ -22,6 +22,19 @@ namespace AZ { + JsonSerializationResult::ResultCode JsonDeserializer::DeserializerDefaultCheck(BaseJsonSerializer* serializer, void* object, + const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context) + { + using namespace AZ::JsonSerializationResult; + + bool isExplicitDefault = IsExplicitDefault(value); + bool manuallyDefaults = (serializer->GetOperationsFlags() & BaseJsonSerializer::OperationFlags::ManualDefault) == + BaseJsonSerializer::OperationFlags::ManualDefault; + return !isExplicitDefault || (isExplicitDefault && manuallyDefaults) + ? serializer->Load(object, typeId, value, context) + : context.Report(Tasks::ReadField, Outcomes::DefaultsUsed, "Value has an explicit default."); + } + JsonSerializationResult::ResultCode JsonDeserializer::Load(void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context) { @@ -33,17 +46,12 @@ namespace AZ "Target object for Json Serialization is pointing to nothing during loading."); } - if (IsExplicitDefault(value)) - { - return context.Report(Tasks::ReadField, Outcomes::DefaultsUsed, "Value has an explicit default."); - } - BaseJsonSerializer* serializer = context.GetRegistrationContext()->GetSerializerForType(typeId); if (serializer) { - return serializer->Load(object, typeId, value, context); + return DeserializerDefaultCheck(serializer, object, typeId, value, context); } - + const SerializeContext::ClassData* classData = context.GetSerializeContext()->FindClassData(typeId); if (!classData) { @@ -56,9 +64,14 @@ namespace AZ serializer = context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId()); if (serializer) { - return serializer->Load(object, typeId, value, context); + return DeserializerDefaultCheck(serializer, object, typeId, value, context); } } + + if (IsExplicitDefault(value)) + { + return context.Report(Tasks::ReadField, Outcomes::DefaultsUsed, "Value has an explicit default."); + } if (classData->m_azRtti && (classData->m_azRtti->GetTypeTraits() & AZ::TypeTraits::is_enum) == AZ::TypeTraits::is_enum) { diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.h b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.h index 89e527b9ae..5954082ee0 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonDeserializer.h @@ -113,5 +113,13 @@ namespace AZ //! Checks if a value is an explicit default. This means the value is an object with no members. static bool IsExplicitDefault(const rapidjson::Value& value); + + private: + static JsonSerializationResult::ResultCode DeserializerDefaultCheck( + BaseJsonSerializer* serializer, + void* object, + const Uuid& typeId, + const rapidjson::Value& value, + JsonDeserializerContext& context); }; } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.cpp index fa244d3dae..437a648e1e 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.cpp @@ -215,10 +215,10 @@ namespace AZ // Load key void* keyAddress = pairContainer->GetElementByIndex(address, pairElement, 0); AZ_Assert(keyAddress, "Element reserved for associative container, but unable to retrieve address of the key."); - Flags keyLoadFlags = Flags::None; + ContinuationFlags keyLoadFlags = ContinuationFlags::None; if (keyElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER) { - keyLoadFlags = Flags::ResolvePointer; + keyLoadFlags = ContinuationFlags::ResolvePointer; *reinterpret_cast(keyAddress) = nullptr; } JSR::ResultCode keyResult = ContinueLoading(keyAddress, keyElement->m_typeId, key, context, keyLoadFlags); @@ -231,10 +231,10 @@ namespace AZ // Load value void* valueAddress = pairContainer->GetElementByIndex(address, pairElement, 1); AZ_Assert(valueAddress, "Element reserved for associative container, but unable to retrieve address of the value."); - Flags valueLoadFlags = Flags::None; + ContinuationFlags valueLoadFlags = ContinuationFlags::None; if (valueElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER) { - valueLoadFlags = Flags::ResolvePointer; + valueLoadFlags = ContinuationFlags::ResolvePointer; *reinterpret_cast(valueAddress) = nullptr; } JSR::ResultCode valueResult = ContinueLoading(valueAddress, valueElement->m_typeId, value, context, valueLoadFlags); diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/SmartPointerSerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/SmartPointerSerializer.cpp index 9e707f8644..0ab32ac08d 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/SmartPointerSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/SmartPointerSerializer.cpp @@ -82,7 +82,7 @@ namespace AZ { // If the target type is the same as the type already stored in the smart pointer than no new // instance is created and the existing instance will be updated with the data in the json document. - result = ContinueLoading(instance, elementClassId, inputValue, context, Flags::ResolvePointer); + result = ContinueLoading(instance, elementClassId, inputValue, context, ContinuationFlags::ResolvePointer); return false; } } @@ -93,7 +93,7 @@ namespace AZ // the wrong address. In these cases explicitly reset the smart pointer. This will erase the existing // data but that's fine as it's not being used. void* element = nullptr; - result = ContinueLoading(&element, elementClassId, inputValue, context, Flags::ResolvePointer); + result = ContinueLoading(&element, elementClassId, inputValue, context, ContinuationFlags::ResolvePointer); if (result.GetProcessing() != JSR::Processing::Halted && result.GetProcessing() != JSR::Processing::Altered) { void* elementPtr = container->ReserveElement(instance, nullptr); @@ -155,8 +155,14 @@ namespace AZ container->EnumElements(const_cast(defaultValue), defaultInputCallback); } - JSR::ResultCode result = ContinueStoring(outputValue, inputValue, defaultValue, inputPtrType, context, Flags::ResolvePointer); + JSR::ResultCode result = + ContinueStoring(outputValue, inputValue, defaultValue, inputPtrType, context, ContinuationFlags::ResolvePointer); return context.Report(result, result.GetProcessing() != JSR::Processing::Halted ? "Successfully processed smart pointer." : "A problem occurred while processing a smart pointer."); } + + BaseJsonSerializer::OperationFlags JsonSmartPointerSerializer::GetOperationsFlags() const + { + return OperationFlags::ManualDefault; + } } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/SmartPointerSerializer.h b/Code/Framework/AzCore/AzCore/Serialization/Json/SmartPointerSerializer.h index 8c550cb824..9a0cf61be9 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/SmartPointerSerializer.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/SmartPointerSerializer.h @@ -28,5 +28,7 @@ namespace AZ JsonDeserializerContext& context) override; JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) override; + + OperationFlags GetOperationsFlags() const override; }; } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/TupleSerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/TupleSerializer.cpp index 9ea22592bc..5b43cec817 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/TupleSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/TupleSerializer.cpp @@ -99,8 +99,9 @@ namespace AZ ScopedContextPath subPath(context, i); - Flags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ? - Flags::ResolvePointer : Flags::None; + ContinuationFlags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER + ? ContinuationFlags::ResolvePointer + : ContinuationFlags::None; JSR::ResultCode result = ContinueStoring(elementValues[i], elementAddress, defaultElementAddress, classElements[i]->m_typeId, context, flags); @@ -179,8 +180,9 @@ namespace AZ void* elementAddress = container->GetElementByIndex(outputValue, nullptr, i); AZ_Assert(elementAddress, "Address of AZStd::pair or AZStd::tuple element %zu could not be retrieved.", i); - Flags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ? - Flags::ResolvePointer : Flags::None; + ContinuationFlags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER + ? ContinuationFlags::ResolvePointer + : ContinuationFlags::None; while (arrayIndex < inputValue.Size()) { diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 5357ed66a6..dc0fb13f00 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -290,6 +290,8 @@ set(FILES Math/MathScriptHelpers.h Math/MathUtils.cpp Math/MathUtils.h + Math/MathMatrixSerializer.h + Math/MathMatrixSerializer.cpp Math/MathVectorSerializer.h Math/MathVectorSerializer.cpp Math/Matrix3x3.cpp diff --git a/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp b/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp index a494207850..e44f77b119 100644 --- a/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/AssetJsonSerializerTests.cpp @@ -104,6 +104,11 @@ namespace JsonSerializationTests AZ::AllocatorInstance::Destroy(); } + void Reflect(AZStd::unique_ptr& context) override + { + context->RegisterGenericType(); + } + AZStd::shared_ptr CreateSerializer() override { return AZStd::make_shared(); diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/BaseJsonSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/BaseJsonSerializerTests.cpp index 08de21b54f..47e05997fc 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/BaseJsonSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/BaseJsonSerializerTests.cpp @@ -119,7 +119,8 @@ namespace JsonSerializationTests int value = 0; int* ptrValue = &value; - ResultCode result = ContinueLoading(&ptrValue, azrtti_typeid(), json, *m_jsonDeserializationContext, Flags::ResolvePointer); + ResultCode result = + ContinueLoading(&ptrValue, azrtti_typeid(), json, *m_jsonDeserializationContext, ContinuationFlags::ResolvePointer); EXPECT_EQ(Processing::Completed, result.GetProcessing()); ASSERT_NE(nullptr, ptrValue); @@ -134,7 +135,8 @@ namespace JsonSerializationTests json.Set(42); int* ptrValue = nullptr; - ResultCode result = ContinueLoading(&ptrValue, azrtti_typeid(), json, *m_jsonDeserializationContext, Flags::ResolvePointer); + ResultCode result = + ContinueLoading(&ptrValue, azrtti_typeid(), json, *m_jsonDeserializationContext, ContinuationFlags::ResolvePointer); EXPECT_EQ(Processing::Completed, result.GetProcessing()); ASSERT_NE(nullptr, ptrValue); @@ -150,7 +152,8 @@ namespace JsonSerializationTests rapidjson::Value json(rapidjson::kObjectType); int* ptrValue = nullptr; - ResultCode result = ContinueLoading(&ptrValue, azrtti_typeid(), json, *m_jsonDeserializationContext, Flags::ResolvePointer); + ResultCode result = + ContinueLoading(&ptrValue, azrtti_typeid(), json, *m_jsonDeserializationContext, ContinuationFlags::ResolvePointer); EXPECT_EQ(Processing::Completed, result.GetProcessing()); ASSERT_NE(nullptr, ptrValue); @@ -165,7 +168,8 @@ namespace JsonSerializationTests rapidjson::Value json(rapidjson::kNullType); int* ptrValue = reinterpret_cast(azmalloc(sizeof(int), alignof(int), AZ::SystemAllocator)); - ResultCode result = ContinueLoading(&ptrValue, azrtti_typeid(), json, *m_jsonDeserializationContext, Flags::ResolvePointer); + ResultCode result = + ContinueLoading(&ptrValue, azrtti_typeid(), json, *m_jsonDeserializationContext, ContinuationFlags::ResolvePointer); EXPECT_EQ(Processing::Completed, result.GetProcessing()); ASSERT_EQ(nullptr, ptrValue); @@ -194,8 +198,8 @@ namespace JsonSerializationTests int value = 42; int* ptrValue = &value; - ResultCode result = ContinueStoring(*m_jsonDocument, &ptrValue, nullptr, azrtti_typeid(), *m_jsonSerializationContext, - Flags::ResolvePointer); + ResultCode result = ContinueStoring( + *m_jsonDocument, &ptrValue, nullptr, azrtti_typeid(), *m_jsonSerializationContext, ContinuationFlags::ResolvePointer); EXPECT_EQ(Processing::Completed, result.GetProcessing()); Expect_DocStrEq("42"); @@ -210,8 +214,9 @@ namespace JsonSerializationTests int value2 = 42; int* defaultPtrValue = &value2; - ResultCode result = - ContinueStoring(*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid(), *m_jsonSerializationContext, Flags::ResolvePointer); + ResultCode result = ContinueStoring( + *m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid(), *m_jsonSerializationContext, + ContinuationFlags::ResolvePointer); EXPECT_EQ(Processing::Completed, result.GetProcessing()); Expect_DocStrEq("{}"); @@ -224,7 +229,7 @@ namespace JsonSerializationTests int* ptrValue = nullptr; ResultCode result = ContinueStoring( - *m_jsonDocument, &ptrValue, nullptr, azrtti_typeid(), *m_jsonSerializationContext, Flags::ResolvePointer); + *m_jsonDocument, &ptrValue, nullptr, azrtti_typeid(), *m_jsonSerializationContext, ContinuationFlags::ResolvePointer); EXPECT_EQ(Processing::Completed, result.GetProcessing()); Expect_DocStrEq("null"); @@ -238,8 +243,9 @@ namespace JsonSerializationTests int value2 = 42; int* defaultPtrValue = &value2; - ResultCode result = - ContinueStoring(*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid(), *m_jsonSerializationContext, Flags::ResolvePointer); + ResultCode result = ContinueStoring( + *m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid(), *m_jsonSerializationContext, + ContinuationFlags::ResolvePointer); EXPECT_EQ(Processing::Completed, result.GetProcessing()); Expect_DocStrEq("null"); @@ -252,8 +258,9 @@ namespace JsonSerializationTests int* ptrValue = nullptr; int* defaultPtrValue = nullptr; - ResultCode result = - ContinueStoring(*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid(), *m_jsonSerializationContext, Flags::ResolvePointer); + ResultCode result = ContinueStoring( + *m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid(), *m_jsonSerializationContext, + ContinuationFlags::ResolvePointer); EXPECT_EQ(Processing::Completed, result.GetProcessing()); Expect_DocStrEq("null"); @@ -265,8 +272,8 @@ namespace JsonSerializationTests int value = 42; - ResultCode result = ContinueStoring(*m_jsonDocument, &value, nullptr, azrtti_typeid(), *m_jsonSerializationContext, - Flags::ReplaceDefault); + ResultCode result = ContinueStoring( + *m_jsonDocument, &value, nullptr, azrtti_typeid(), *m_jsonSerializationContext, ContinuationFlags::ReplaceDefault); EXPECT_EQ(Processing::Completed, result.GetProcessing()); Expect_DocStrEq("42"); @@ -280,7 +287,7 @@ namespace JsonSerializationTests int* ptrValue = &value; ResultCode result = ContinueStoring(*m_jsonDocument, &ptrValue, nullptr, azrtti_typeid(), *m_jsonSerializationContext, - Flags::ResolvePointer | Flags::ReplaceDefault); + ContinuationFlags::ResolvePointer | ContinuationFlags::ReplaceDefault); EXPECT_EQ(Processing::Completed, result.GetProcessing()); Expect_DocStrEq("42"); @@ -293,8 +300,8 @@ namespace JsonSerializationTests int value = 42; AZ::Uuid unknownType("{09AE3CEC-EBFC-41EC-A7F6-949721521716}"); - ResultCode result = ContinueStoring(*m_jsonDocument, &value, nullptr, unknownType, *m_jsonSerializationContext, - Flags::ReplaceDefault); + ResultCode result = + ContinueStoring(*m_jsonDocument, &value, nullptr, unknownType, *m_jsonSerializationContext, ContinuationFlags::ReplaceDefault); EXPECT_EQ(Processing::Halted, result.GetProcessing()); } diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/JsonSerializerConformityTests.h b/Code/Framework/AzCore/Tests/Serialization/Json/JsonSerializerConformityTests.h index 4f0825ff1b..c0f470378c 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/JsonSerializerConformityTests.h +++ b/Code/Framework/AzCore/Tests/Serialization/Json/JsonSerializerConformityTests.h @@ -90,9 +90,14 @@ namespace JsonSerializationTests virtual ~JsonSerializerConformityTestDescriptor() = default; virtual AZStd::shared_ptr CreateSerializer() = 0; - + //! Create an instance of the target type with all values set to default. virtual AZStd::shared_ptr CreateDefaultInstance() = 0; + //! Create an instance of the target type that constructed with default constructor. + //! This will be the same instance that Json Serialization creates for dynamic types. Typically it's the same + //! as from CreateDefaultInstance(), except of types, such as pointers, that need to do minimal (de)serialization + //! to initialize an object. + virtual AZStd::shared_ptr CreateDefaultConstructedInstance() { return CreateDefaultInstance(); } //! Create an instance of the target type with some values set and some kept on defaults. //! If the target type doesn't support partial specialization this can be ignored and //! tests for partial support will be skipped. @@ -316,10 +321,10 @@ namespace JsonSerializationTests ASSERT_FALSE(this->m_jsonDocument->HasParseError()); auto serializer = this->m_description.CreateSerializer(); - auto instance = this->m_description.CreateDefaultInstance(); + auto instance = this->m_description.CreateDefaultConstructedInstance(); auto original = this->m_description.CreateDefaultInstance(); - ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*original), + ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext); if (this->m_features.m_mandatoryFields.empty()) @@ -339,6 +344,42 @@ namespace JsonSerializationTests } } + TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeEmptyObjectThroughMainLoad_SucceedsAndObjectMatchesDefaults) + { + using namespace AZ::JsonSerializationResult; + + if (this->m_features.SupportsJsonType(rapidjson::kObjectType)) + { + this->m_jsonDocument->Parse("{}"); + ASSERT_FALSE(this->m_jsonDocument->HasParseError()); + + auto serializer = this->m_description.CreateSerializer(); + auto instance = this->m_description.CreateDefaultConstructedInstance(); + auto original = this->m_description.CreateDefaultInstance(); + + AZ::JsonDeserializerSettings settings; + settings.m_serializeContext = this->m_jsonDeserializationContext->GetSerializeContext(); + settings.m_registrationContext = this->m_jsonDeserializationContext->GetRegistrationContext(); + ResultCode result = AZ::JsonSerialization::Load( + instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, settings); + + if (this->m_features.m_mandatoryFields.empty()) + { + EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome()); + EXPECT_EQ(Processing::Completed, result.GetProcessing()); + } + else + { + EXPECT_EQ(Outcomes::Unsupported, result.GetOutcome()); + bool validProcessing = + result.GetProcessing() == Processing::Altered || + result.GetProcessing() == Processing::PartialAlter; + EXPECT_TRUE(validProcessing); + } + EXPECT_TRUE(this->m_description.AreEqual(*original, *instance)); + } + } + TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeEmptyArray_SucceedsAndObjectMatchesDefaults) { using namespace AZ::JsonSerializationResult; @@ -349,7 +390,7 @@ namespace JsonSerializationTests ASSERT_FALSE(this->m_jsonDocument->HasParseError()); auto serializer = this->m_description.CreateSerializer(); - auto instance = this->m_description.CreateDefaultInstance(); + auto instance = this->m_description.CreateDefaultConstructedInstance(); auto original = this->m_description.CreateDefaultInstance(); this->m_deserializationSettings->m_clearContainers = false; @@ -384,7 +425,7 @@ namespace JsonSerializationTests ASSERT_FALSE(this->m_jsonDocument->HasParseError()); auto serializer = this->m_description.CreateSerializer(); - auto instance = this->m_description.CreateDefaultInstance(); + auto instance = this->m_description.CreateDefaultConstructedInstance(); auto original = this->m_description.CreateDefaultInstance(); this->m_deserializationSettings->m_clearContainers = true; @@ -488,7 +529,7 @@ namespace JsonSerializationTests ASSERT_FALSE(this->m_jsonDocument->HasParseError()); auto serializer = this->m_description.CreateSerializer(); - auto instance = this->m_description.CreateDefaultInstance(); + auto instance = this->m_description.CreateDefaultConstructedInstance(); auto compare = this->m_description.CreateFullySetInstance(); ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance), @@ -499,6 +540,28 @@ namespace JsonSerializationTests EXPECT_TRUE(this->m_description.AreEqual(*instance, *compare)); } + TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeFullySetInstanceThroughMainLoad_SucceedsAndObjectMatchesFullySetInstance) + { + using namespace AZ::JsonSerializationResult; + + AZStd::string_view json = this->m_description.GetJsonFor_Load_DeserializeFullySetInstance(); + this->m_jsonDocument->Parse(json.data()); + ASSERT_FALSE(this->m_jsonDocument->HasParseError()); + + auto serializer = this->m_description.CreateSerializer(); + auto instance = this->m_description.CreateDefaultConstructedInstance(); + auto compare = this->m_description.CreateFullySetInstance(); + + AZ::JsonDeserializerSettings settings; + settings.m_serializeContext = this->m_jsonDeserializationContext->GetSerializeContext(); + settings.m_registrationContext = this->m_jsonDeserializationContext->GetRegistrationContext(); + ResultCode result = AZ::JsonSerialization::Load(instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, settings); + + EXPECT_EQ(Outcomes::Success, result.GetOutcome()); + EXPECT_EQ(Processing::Completed, result.GetProcessing()); + EXPECT_TRUE(this->m_description.AreEqual(*instance, *compare)); + } + TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeWithMissingMandatoryField_LoadFailedAndUnsupportedReported) { using namespace AZ::JsonSerializationResult; @@ -518,7 +581,7 @@ namespace JsonSerializationTests ASSERT_NE(this->m_jsonDocument->MemberEnd(), memberToErase); this->m_jsonDocument->RemoveMember(memberToErase); - auto instance = this->m_description.CreateDefaultInstance(); + auto instance = this->m_description.CreateDefaultConstructedInstance(); ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext); @@ -546,7 +609,7 @@ namespace JsonSerializationTests ASSERT_FALSE(this->m_jsonDocument->HasParseError()); auto serializer = this->m_description.CreateSerializer(); - auto instance = this->m_description.CreateDefaultInstance(); + auto instance = this->m_description.CreateDefaultConstructedInstance(); auto compare = this->m_description.CreatePartialDefaultInstance(); ASSERT_NE(nullptr, compare); @@ -567,7 +630,7 @@ namespace JsonSerializationTests ASSERT_FALSE(this->m_jsonDocument->HasParseError()); auto serializer = this->m_description.CreateSerializer(); - auto instance = this->m_description.CreateDefaultInstance(); + auto instance = this->m_description.CreateDefaultConstructedInstance(); AZ::ScopedContextReporter reporter(*this->m_jsonDeserializationContext, [](AZStd::string_view message, ResultCode result, AZStd::string_view path) -> ResultCode @@ -604,7 +667,7 @@ namespace JsonSerializationTests } auto serializer = this->m_description.CreateSerializer(); - auto instance = this->m_description.CreateDefaultInstance(); + auto instance = this->m_description.CreateDefaultConstructedInstance(); auto compare = this->m_description.CreateFullySetInstance(); ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance), @@ -635,7 +698,7 @@ namespace JsonSerializationTests } auto serializer = this->m_description.CreateSerializer(); - auto instance = this->m_description.CreateDefaultInstance(); + auto instance = this->m_description.CreateDefaultConstructedInstance(); ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext); @@ -693,6 +756,36 @@ namespace JsonSerializationTests } } + TYPED_TEST_P(JsonSerializerConformityTests, Store_SerializeDefaultInstanceThroughMainStore_EmptyJsonReturned) + { + using namespace AZ::JsonSerializationResult; + + auto serializer = this->m_description.CreateSerializer(); + auto instance = this->m_description.CreateDefaultInstance(); + rapidjson::Value convertedValue = this->CreateExplicitDefault(); + + AZ::JsonSerializerSettings settings; + settings.m_serializeContext = this->m_jsonDeserializationContext->GetSerializeContext(); + settings.m_registrationContext = this->m_jsonDeserializationContext->GetRegistrationContext(); + ResultCode result = AZ::JsonSerialization::Store( + convertedValue, this->m_jsonDocument->GetAllocator(), instance.get(), instance.get(), azrtti_typeid(*instance), settings); + + EXPECT_EQ(Processing::Completed, result.GetProcessing()); + if (convertedValue.IsObject() && !this->m_features.m_mandatoryFields.empty()) + { + ASSERT_EQ(convertedValue.MemberCount(), this->m_features.m_mandatoryFields.size()); + for (const AZStd::string& mandatoryField : this->m_features.m_mandatoryFields) + { + EXPECT_NE(convertedValue.MemberEnd(), convertedValue.FindMember(mandatoryField.c_str())); + } + } + else + { + EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome()); + this->Expect_ExplicitDefault(convertedValue); + } + } + TYPED_TEST_P(JsonSerializerConformityTests, Store_SerializeWithDefaultsKept_FullyWrittenJson) { using namespace AZ::JsonSerializationResult; @@ -924,6 +1017,20 @@ namespace JsonSerializationTests } } + TYPED_TEST_P(JsonSerializerConformityTests, GetOperationsFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared) + { + if (this->m_features.SupportsJsonType(rapidjson::kObjectType)) + { + if (!this->m_features.m_mandatoryFields.empty()) + { + auto serializer = this->m_description.CreateSerializer(); + bool manuallyHandlesDefaults = (serializer->GetOperationsFlags() & AZ::BaseJsonSerializer::OperationFlags::ManualDefault) == + AZ::BaseJsonSerializer::OperationFlags::ManualDefault; + EXPECT_TRUE(manuallyHandlesDefaults); + } + } + } + REGISTER_TYPED_TEST_CASE_P(JsonSerializerConformityTests, Registration_SerializerIsRegisteredWithContext_SerializerFound, @@ -934,14 +1041,16 @@ namespace JsonSerializationTests Load_InvalidTypeOfArrayType_ReturnsUnsupported, Load_InvalidTypeOfStringType_ReturnsUnsupported, Load_InvalidTypeOfNumberType_ReturnsUnsupported, - + Load_DeserializeUnreflectedType_ReturnsUnsupported, Load_DeserializeEmptyObject_SucceedsAndObjectMatchesDefaults, + Load_DeserializeEmptyObjectThroughMainLoad_SucceedsAndObjectMatchesDefaults, Load_DeserializeEmptyArray_SucceedsAndObjectMatchesDefaults, Load_DeserializeEmptyArrayWithClearEnabled_SucceedsAndObjectMatchesDefaults, Load_DeserializeEmptyArrayWithClearedTarget_SucceedsAndObjectMatchesDefaults, Load_InterruptClearingTarget_ContainerIsNotCleared, Load_DeserializeFullySetInstance_SucceedsAndObjectMatchesFullySetInstance, + Load_DeserializeFullySetInstanceThroughMainLoad_SucceedsAndObjectMatchesFullySetInstance, Load_DeserializePartialInstance_SucceedsAndObjectMatchesParialInstance, Load_DeserializeWithMissingMandatoryField_LoadFailedAndUnsupportedReported, Load_InsertAdditionalData_SucceedsAndObjectMatchesFullySetInstance, @@ -950,6 +1059,7 @@ namespace JsonSerializationTests Store_SerializeUnreflectedType_ReturnsUnsupported, Store_SerializeDefaultInstance_EmptyJsonReturned, + Store_SerializeDefaultInstanceThroughMainStore_EmptyJsonReturned, Store_SerializeWithDefaultsKept_FullyWrittenJson, Store_SerializeFullySetInstance_StoredSuccessfullyAndJsonMatches, Store_SerializeWithoutDefault_StoredSuccessfullyAndJsonMatches, @@ -957,10 +1067,12 @@ namespace JsonSerializationTests Store_SerializePartialInstance_StoredSuccessfullyAndJsonMatches, Store_SerializeEmptyArray_StoredSuccessfullyAndJsonMatches, Store_HaltedThroughCallback_StoreFailsAndHaltReported, - + StoreLoad_RoundTripWithPartialDefault_IdenticalInstances, StoreLoad_RoundTripWithFullSet_IdenticalInstances, - StoreLoad_RoundTripWithDefaultsKept_IdenticalInstances); + StoreLoad_RoundTripWithDefaultsKept_IdenticalInstances, + + GetOperationsFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared); } // namespace JsonSerializationTests namespace AZ diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/MathMatrixSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/MathMatrixSerializerTests.cpp new file mode 100644 index 0000000000..b9d1edab76 --- /dev/null +++ b/Code/Framework/AzCore/Tests/Serialization/Json/MathMatrixSerializerTests.cpp @@ -0,0 +1,562 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace JsonSerializationTests +{ + namespace DataHelper + { + // Build Matrix + + template + MatrixType BuildMatrixRotationWithSale(const AZ::Vector3& angles, float scale) + { + // start a matrix with angle degrees + const AZ::Vector3 eulerRadians = AZ::Vector3DegToRad(angles); + const auto rotX = MatrixType::CreateRotationX(eulerRadians.GetX()); + const auto rotY = MatrixType::CreateRotationY(eulerRadians.GetY()); + const auto rotZ = MatrixType::CreateRotationZ(eulerRadians.GetZ()); + auto matrix = rotX * rotY * rotZ; + + // apply a scale + matrix.MultiplyByScale(AZ::Vector3{ scale }); + return matrix; + } + + template + MatrixType BuildMatrix(const AZ::Vector3& angles, float scale, const AZ::Vector3& translation) + { + auto matrix = BuildMatrixRotationWithSale(angles, scale); + matrix.SetTranslation(translation); + return matrix; + } + + template <> + AZ::Matrix3x3 BuildMatrix(const AZ::Vector3& angles, float scale, const AZ::Vector3&) + { + return BuildMatrixRotationWithSale(angles, scale); + } + + // Arbitrary Matrix + + template + MatrixType CreateArbitraryMatrixRotationAndSale(AZ::SimpleLcgRandom& random) + { + // start a matrix with arbitrary degrees + float roll = random.GetRandomFloat() * 360.0f; + float pitch = random.GetRandomFloat() * 360.0f; + float yaw = random.GetRandomFloat() * 360.0f; + const AZ::Vector3 eulerRadians = AZ::Vector3DegToRad(AZ::Vector3{ roll, pitch, yaw }); + const auto rotX = MatrixType::CreateRotationX(eulerRadians.GetX()); + const auto rotY = MatrixType::CreateRotationY(eulerRadians.GetY()); + const auto rotZ = MatrixType::CreateRotationZ(eulerRadians.GetZ()); + auto matrix = rotX * rotY * rotZ; + + // apply a scale + matrix.MultiplyByScale(AZ::Vector3{ random.GetRandomFloat() }); + return matrix; + } + + template + void AssignArbitrarySetTranslation(MatrixType& matrix, AZ::SimpleLcgRandom& random) + { + float x = random.GetRandomFloat() * 10000.0f; + float y = random.GetRandomFloat() * 10000.0f; + float z = random.GetRandomFloat() * 10000.0f; + matrix.SetTranslation(AZ::Vector3{ x, y, z }); + } + + template + MatrixType CreateArbitraryMatrix(size_t seed); + + template <> + AZ::Matrix3x3 CreateArbitraryMatrix(size_t seed) + { + AZ::SimpleLcgRandom random(seed); + return CreateArbitraryMatrixRotationAndSale(random); + } + + template <> + AZ::Matrix3x4 CreateArbitraryMatrix(size_t seed) + { + AZ::SimpleLcgRandom random(seed); + auto matrix = CreateArbitraryMatrixRotationAndSale(random); + AssignArbitrarySetTranslation(matrix, random); + return matrix; + } + + template <> + AZ::Matrix4x4 CreateArbitraryMatrix(size_t seed) + { + AZ::SimpleLcgRandom random(seed); + auto matrix = CreateArbitraryMatrixRotationAndSale(random); + AssignArbitrarySetTranslation(matrix, random); + return matrix; + } + + // CreateQuaternion + + template + AZ::Quaternion CreateQuaternion(const MatrixType& matrix); + + template<> + AZ::Quaternion CreateQuaternion(const AZ::Matrix3x3& matrix) + { + return AZ::Quaternion::CreateFromMatrix3x3(matrix); + } + + template<> + AZ::Quaternion CreateQuaternion(const AZ::Matrix3x4& matrix) + { + return AZ::Quaternion::CreateFromMatrix3x4(matrix); + } + + template<> + AZ::Quaternion CreateQuaternion(const AZ::Matrix4x4& matrix) + { + return AZ::Quaternion::CreateFromMatrix4x4(matrix); + } + + template + void AddRotation(rapidjson::Value& value, const MatrixType& matrix, rapidjson::Document::AllocatorType& allocator) + { + AZ::Quaternion rotation = CreateQuaternion(matrix); + const auto degrees = rotation.GetEulerDegrees(); + value.AddMember("yaw", degrees.GetX(), allocator); + value.AddMember("pitch", degrees.GetY(), allocator); + value.AddMember("roll", degrees.GetZ(), allocator); + } + + void AddScale(rapidjson::Value& value, float scale, rapidjson::Document::AllocatorType& allocator) + { + value.AddMember("scale", scale, allocator); + } + + void AddTranslation(rapidjson::Value& value, const AZ::Vector3& translation, rapidjson::Document::AllocatorType& allocator) + { + value.AddMember("x", translation.GetX(), allocator); + value.AddMember("y", translation.GetY(), allocator); + value.AddMember("z", translation.GetZ(), allocator); + } + + template + void AddData(rapidjson::Value& value, const MatrixType& matrix, rapidjson::Document::AllocatorType& allocator); + + template <> + void AddData(rapidjson::Value& value, const AZ::Matrix3x3& matrix, rapidjson::Document::AllocatorType& allocator) + { + AddScale(value, matrix.RetrieveScale().GetX(), allocator); + AddRotation(value, matrix, allocator); + } + + template <> + void AddData(rapidjson::Value& value, const AZ::Matrix3x4& matrix, rapidjson::Document::AllocatorType& allocator) + { + AddScale(value, matrix.RetrieveScale().GetX(), allocator); + AddTranslation(value, matrix.GetTranslation(), allocator); + AddRotation(value, matrix, allocator); + } + + template <> + void AddData(rapidjson::Value& value, const AZ::Matrix4x4& matrix, rapidjson::Document::AllocatorType& allocator) + { + AddScale(value, matrix.RetrieveScale().GetX(), allocator); + AddTranslation(value, matrix.GetTranslation(), allocator); + AddRotation(value, matrix, allocator); + } + }; + + template + class MathMatrixSerializerTestDescription : + public JsonSerializerConformityTestDescriptor + { + public: + AZStd::shared_ptr CreateSerializer() override + { + return AZStd::make_shared(); + } + + AZStd::shared_ptr CreateDefaultInstance() override + { + return AZStd::make_shared(MatrixType::CreateIdentity()); + } + + AZStd::shared_ptr CreateFullySetInstance() override + { + auto angles = AZ::Vector3 { 0.0f, 0.0f, 0.0f }; + auto scale = 10.0f; + auto translation = AZ::Vector3{ 10.0f, 20.0f, 30.0f }; + auto matrix = DataHelper::BuildMatrix(angles, scale, translation); + return AZStd::make_shared(matrix); + } + + AZStd::string_view GetJsonForFullySetInstance() override + { + if constexpr (RowCount * ColumnCount == 9) + { + return "{\"roll\":0.0,\"pitch\":0.0,\"yaw\":0.0,\"scale\":10.0}"; + } + else if constexpr (RowCount * ColumnCount == 12) + { + return "{\"roll\":0.0,\"pitch\":0.0,\"yaw\":0.0,\"scale\":10.0,\"x\":10.0,\"y\":20.0,\"z\":30.0}"; + } + else if constexpr (RowCount * ColumnCount == 16) + { + return "{\"roll\":0.0,\"pitch\":0.0,\"yaw\":0.0,\"scale\":10.0,\"x\":10.0,\"y\":20.0,\"z\":30.0}"; + } + else + { + static_assert((RowCount >= 3 && RowCount <= 4) && (ColumnCount >= 3 && ColumnCount <= 4), + "Only matrix 3x3, 3x4 or 4x4 are supported by this test."); + } + return "{}"; + } + + void ConfigureFeatures(JsonSerializerConformityTestDescriptorFeatures& features) override + { + features.EnableJsonType(rapidjson::kArrayType); + features.EnableJsonType(rapidjson::kObjectType); + features.m_fixedSizeArray = true; + features.m_supportsPartialInitialization = false; + features.m_supportsInjection = false; + } + + bool AreEqual(const MatrixType& lhs, const MatrixType& rhs) override + { + for (int r = 0; r < RowCount; ++r) + { + for (int c = 0; c < ColumnCount; ++c) + { + if (!AZ::IsClose(lhs.GetElement(r, c), rhs.GetElement(r, c), AZ::Constants::Tolerance)) + { + return false; + } + } + } + return true; + } + }; + + using MathMatrixSerializerConformityTestTypes = ::testing::Types< + MathMatrixSerializerTestDescription, + MathMatrixSerializerTestDescription, + MathMatrixSerializerTestDescription + >; + INSTANTIATE_TYPED_TEST_CASE_P(JsonMathMatrixSerializer, JsonSerializerConformityTests, MathMatrixSerializerConformityTestTypes); + + template + class JsonMathMatrixSerializerTests + : public BaseJsonSerializerFixture + { + public: + using Descriptor = T; + + void SetUp() override + { + BaseJsonSerializerFixture::SetUp(); + m_serializer = AZStd::make_unique(); + } + + void TearDown() override + { + m_serializer.reset(); + BaseJsonSerializerFixture::TearDown(); + } + + protected: + AZStd::unique_ptr m_serializer; + }; + + struct Matrix3x3Descriptor + { + using MatrixType = AZ::Matrix3x3; + using Serializer = AZ::JsonMatrix3x3Serializer; + constexpr static size_t RowCount = 3; + constexpr static size_t ColumnCount = 3; + constexpr static size_t ElementCount = RowCount * ColumnCount; + constexpr static bool HasTranslation = false; + }; + + struct Matrix3x4Descriptor + { + using MatrixType = AZ::Matrix3x4; + using Serializer = AZ::JsonMatrix3x4Serializer; + constexpr static size_t RowCount = 3; + constexpr static size_t ColumnCount = 4; + constexpr static size_t ElementCount = RowCount * ColumnCount; + constexpr static bool HasTranslation = true; + }; + + struct Matrix4x4Descriptor + { + using MatrixType = AZ::Matrix4x4; + using Serializer = AZ::JsonMatrix4x4Serializer; + constexpr static size_t RowCount = 4; + constexpr static size_t ColumnCount = 4; + constexpr static size_t ElementCount = RowCount * ColumnCount; + constexpr static bool HasTranslation = true; + }; + + using JsonMathMatrixSerializerTypes = ::testing::Types < + Matrix3x3Descriptor, Matrix3x4Descriptor, Matrix4x4Descriptor>; + TYPED_TEST_CASE(JsonMathMatrixSerializerTests, JsonMathMatrixSerializerTypes); + + // Load array tests + + TYPED_TEST(JsonMathMatrixSerializerTests, Load_Array_ReturnsConvertAndLoadsMatrix) + { + using namespace AZ::JsonSerializationResult; + + rapidjson::Value& arrayValue = this->m_jsonDocument->SetArray(); + for (size_t i = 0; i < JsonMathMatrixSerializerTests::Descriptor::ElementCount; ++i) + { + arrayValue.PushBack(static_cast(i + 1), this->m_jsonDocument->GetAllocator()); + } + + auto output = JsonMathMatrixSerializerTests::Descriptor::MatrixType::CreateZero(); + ResultCode result = this->m_serializer->Load( + &output, + azrtti_typeid::Descriptor::MatrixType>(), + *this->m_jsonDocument, + *this->m_jsonDeserializationContext); + ASSERT_EQ(Outcomes::Success, result.GetOutcome()); + + for (int r = 0; r < JsonMathMatrixSerializerTests::Descriptor::RowCount; ++r) + { + for (int c = 0; c < JsonMathMatrixSerializerTests::Descriptor::ColumnCount; ++c) + { + auto testValue = static_cast((r * JsonMathMatrixSerializerTests::Descriptor::ColumnCount) + c + 1); + EXPECT_FLOAT_EQ(testValue, output.GetElement(r, c)); + } + } + } + + TYPED_TEST(JsonMathMatrixSerializerTests, Load_InvalidEntries_ReturnsUnsupportedAndLeavesMatrixUntouched) + { + using namespace AZ::JsonSerializationResult; + + rapidjson::Value& arrayValue = this->m_jsonDocument->SetArray(); + for (size_t i = 0; i < JsonMathMatrixSerializerTests::Descriptor::ElementCount; ++i) + { + if (i == 1) + { + arrayValue.PushBack(rapidjson::StringRef("Invalid"), this->m_jsonDocument->GetAllocator()); + } + else + { + arrayValue.PushBack(static_cast(i + 1), this->m_jsonDocument->GetAllocator()); + } + } + + auto output = JsonMathMatrixSerializerTests::Descriptor::MatrixType::CreateZero(); + ResultCode result = this->m_serializer->Load( + &output, + azrtti_typeid::Descriptor::MatrixType>(), + *this->m_jsonDocument, + *this->m_jsonDeserializationContext); + EXPECT_EQ(Outcomes::Unsupported, result.GetOutcome()); + + for (int r = 0; r < JsonMathMatrixSerializerTests::Descriptor::RowCount; ++r) + { + for (int c = 0; c < JsonMathMatrixSerializerTests::Descriptor::ColumnCount; ++c) + { + EXPECT_FLOAT_EQ(0.0f, output.GetElement(r, c)); + } + } + } + + TYPED_TEST(JsonMathMatrixSerializerTests, Load_FloatSerializerMissingForArray_ReturnsCatastrophic) + { + using namespace AZ::JsonSerializationResult; + + this->m_jsonRegistrationContext->EnableRemoveReflection(); + this->m_jsonRegistrationContext->template Serializer()->template HandlesType(); + this->m_jsonRegistrationContext->DisableRemoveReflection(); + + rapidjson::Value& arrayValue = this->m_jsonDocument->SetArray(); + for (size_t i = 0; i < JsonMathMatrixSerializerTests::Descriptor::ElementCount + 1; ++i) + { + arrayValue.PushBack(static_cast(i + 1), this->m_jsonDocument->GetAllocator()); + } + + typename JsonMathMatrixSerializerTests::Descriptor::MatrixType output; + ResultCode result = this->m_serializer->Load( + &output, + azrtti_typeid::Descriptor::MatrixType>(), + *this->m_jsonDocument, + *this->m_jsonDeserializationContext); + EXPECT_EQ(Outcomes::Catastrophic, result.GetOutcome()); + + this->m_jsonRegistrationContext->template Serializer()->template HandlesType(); + } + + // Load object tests + TYPED_TEST(JsonMathMatrixSerializerTests, Load_ValidObjectLowerCase_ReturnsSuccessAndLoadsMatrix) + { + using namespace AZ::JsonSerializationResult; + + rapidjson::Value& objectValue = this->m_jsonDocument->SetObject(); + auto input = JsonMathMatrixSerializerTests::Descriptor::MatrixType::CreateIdentity(); + DataHelper::AddData(objectValue, input, this->m_jsonDocument->GetAllocator()); + + auto output = JsonMathMatrixSerializerTests::Descriptor::MatrixType::CreateZero(); + ResultCode result = this->m_serializer->Load( + &output, + azrtti_typeid::Descriptor::MatrixType>(), + *this->m_jsonDocument, + *this->m_jsonDeserializationContext); + ASSERT_EQ(Outcomes::DefaultsUsed, result.GetOutcome()); + EXPECT_TRUE(input == output); + } + + TYPED_TEST(JsonMathMatrixSerializerTests, Load_ValidObjectWithExtraFields_ReturnsPartialConvertAndLoadsMatrix) + { + using namespace AZ::JsonSerializationResult; + + rapidjson::Value& objectValue = this->m_jsonDocument->SetObject(); + auto input = JsonMathMatrixSerializerTests::Descriptor::MatrixType::CreateIdentity(); + DataHelper::AddScale(objectValue, input.RetrieveScale().GetX(), this->m_jsonDocument->GetAllocator()); + DataHelper::AddRotation(objectValue, input, this->m_jsonDocument->GetAllocator()); + objectValue.AddMember(rapidjson::StringRef("extra"), "no value", this->m_jsonDocument->GetAllocator()); + + auto output = JsonMathMatrixSerializerTests::Descriptor::MatrixType::CreateZero(); + ResultCode result = this->m_serializer->Load( + &output, + azrtti_typeid::Descriptor::MatrixType>(), + *this->m_jsonDocument, + *this->m_jsonDeserializationContext); + ASSERT_EQ(Outcomes::DefaultsUsed, result.GetOutcome()); + EXPECT_TRUE(input == output); + } + + TYPED_TEST(JsonMathMatrixSerializerTests, SaveLoad_Identity_LoadsDefaultMatrixWithIdentity) + { + using namespace AZ::JsonSerializationResult; + + auto defaultValue = JsonMathMatrixSerializerTests::Descriptor::MatrixType::CreateIdentity(); + + rapidjson::Value& objectInput = this->m_jsonDocument->SetObject(); + this->m_serializer->Store( + objectInput, + &defaultValue, + &defaultValue, + azrtti_typeid::Descriptor::MatrixType>(), + *this->m_jsonSerializationContext); + + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + objectInput.Accept(writer); + + auto output = defaultValue; + ResultCode result = this->m_serializer->Load( + &output, + azrtti_typeid::Descriptor::MatrixType>(), + *this->m_jsonDocument, + *this->m_jsonDeserializationContext); + + EXPECT_TRUE(defaultValue == output); + } + + TYPED_TEST(JsonMathMatrixSerializerTests, LoadSave_Zero_SavesAndLoadsIdentityMatrix) + { + using namespace AZ::JsonSerializationResult; + + auto defaultValue = JsonMathMatrixSerializerTests::Descriptor::MatrixType::CreateIdentity(); + auto input = JsonMathMatrixSerializerTests::Descriptor::MatrixType::CreateZero(); + + rapidjson::Value& objectInput = this->m_jsonDocument->SetObject(); + this->m_serializer->Store( + objectInput, + &input, + &defaultValue, + azrtti_typeid::Descriptor::MatrixType>(), + *this->m_jsonSerializationContext); + + auto output = defaultValue; + ResultCode result = this->m_serializer->Load( + &output, + azrtti_typeid::Descriptor::MatrixType>(), + *this->m_jsonDocument, + *this->m_jsonDeserializationContext); + + ASSERT_EQ(Outcomes::Unsupported, result.GetOutcome()); + EXPECT_TRUE(defaultValue == output); + } + + TYPED_TEST(JsonMathMatrixSerializerTests, Load_InvalidFields_ReturnsUnsupportedAndLeavesMatrixUntouched) + { + using namespace AZ::JsonSerializationResult; + using Descriptor = typename JsonMathMatrixSerializerTests::Descriptor; + + const auto defaultValue = Descriptor::MatrixType::CreateIdentity(); + rapidjson::Value& objectValue = this->m_jsonDocument->SetObject(); + auto input = Descriptor::MatrixType::CreateIdentity(); + DataHelper::AddData(objectValue, input, this->m_jsonDocument->GetAllocator()); + objectValue["yaw"] = "Invalid"; + + auto output = Descriptor::MatrixType::CreateZero(); + ResultCode result = this->m_serializer->Load( + &output, + azrtti_typeid(), + *this->m_jsonDocument, + *this->m_jsonDeserializationContext); + ASSERT_EQ(Outcomes::Unsupported, result.GetOutcome()); + EXPECT_TRUE(input == output); + } + + TYPED_TEST(JsonMathMatrixSerializerTests, LoadSave_Arbitrary_SavesAndLoadsArbitraryMatrix) + { + using namespace AZ::JsonSerializationResult; + using Descriptor = typename JsonMathMatrixSerializerTests::Descriptor; + + auto defaultValue = Descriptor::MatrixType::CreateIdentity(); + size_t elementCount = Descriptor::RowCount * Descriptor::ColumnCount; + auto input = DataHelper::CreateArbitraryMatrix(elementCount); + + rapidjson::Value& objectInput = this->m_jsonDocument->SetObject(); + this->m_serializer->Store( + objectInput, + &input, + &defaultValue, + azrtti_typeid(), + *this->m_jsonSerializationContext); + + auto output = defaultValue; + ResultCode result = this->m_serializer->Load( + &output, + azrtti_typeid(), + *this->m_jsonDocument, + *this->m_jsonDeserializationContext); + + EXPECT_EQ(Processing::Completed, result.GetProcessing()); + + for (int r = 0; r < Descriptor::RowCount; ++r) + { + for (int c = 0; c < Descriptor::ColumnCount; ++c) + { + EXPECT_NEAR(input.GetElement(r, c), output.GetElement(r, c), AZ::Constants::Tolerance); + } + } + } + +} // namespace JsonSerializationTests diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/SmartPointerSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/SmartPointerSerializerTests.cpp index 2fc131aae2..75391d81d4 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/SmartPointerSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/SmartPointerSerializerTests.cpp @@ -32,6 +32,11 @@ namespace JsonSerializationTests return AZStd::make_shared(); } + AZStd::shared_ptr CreateDefaultConstructedInstance() override + { + return AZStd::make_shared(); + } + void Reflect(AZStd::unique_ptr& context) override { context->RegisterGenericType(); @@ -228,13 +233,19 @@ namespace JsonSerializationTests public: using SmartPointer = typename SmartPointerSimpleDerivedClassTestDescription::SmartPointer; - AZStd::shared_ptr CreateDefaultInstance() override + // This test is specific for derived classes being used as a default value. + AZStd::shared_ptr CreateDefaultConstructedInstance() override { auto result = AZStd::make_shared(); *result = SmartPointer(aznew SimpleInheritence()); return result; } + AZStd::shared_ptr CreateDefaultInstance() override + { + return CreateDefaultConstructedInstance(); + } + AZStd::string_view GetJsonForPartialDefaultInstance() override { return R"( @@ -386,13 +397,19 @@ namespace JsonSerializationTests public: using SmartPointer = typename SmartPointerComplexDerivedClassTestDescription::SmartPointer; - AZStd::shared_ptr CreateDefaultInstance() override + // This test is specific for derived classes being used as a default value. + AZStd::shared_ptr CreateDefaultConstructedInstance() override { auto result = AZStd::make_shared(); *result = SmartPointer(aznew MultipleInheritence()); return result; } + AZStd::shared_ptr CreateDefaultInstance() override + { + return CreateDefaultConstructedInstance(); + } + AZStd::string_view GetJsonForPartialDefaultInstance() override { return R"( diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index 2129761bfe..f90717d003 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -111,6 +111,7 @@ set(FILES Serialization/Json/JsonSerializerMock.h Serialization/Json/MapSerializerTests.cpp Serialization/Json/MathVectorSerializerTests.cpp + Serialization/Json/MathMatrixSerializerTests.cpp Serialization/Json/SmartPointerSerializerTests.cpp Serialization/Json/StringSerializerTests.cpp Serialization/Json/TestCases.h diff --git a/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.cpp b/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.cpp index 095d986fa1..57f14ddb38 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Components/NonUniformScaleComponent.cpp @@ -36,6 +36,8 @@ namespace AzFramework void NonUniformScaleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { + incompatible.push_back(AZ_CRC_CE("NonUniformScaleService")); + incompatible.push_back(AZ_CRC_CE("DebugDrawObbService")); incompatible.push_back(AZ_CRC_CE("DebugDrawService")); incompatible.push_back(AZ_CRC_CE("EMotionFXActorService")); diff --git a/Code/Framework/AzFramework/AzFramework/Physics/WorldBodyBus.h b/Code/Framework/AzFramework/AzFramework/Physics/Components/SimulatedBodyComponentBus.h similarity index 54% rename from Code/Framework/AzFramework/AzFramework/Physics/WorldBodyBus.h rename to Code/Framework/AzFramework/AzFramework/Physics/Components/SimulatedBodyComponentBus.h index 943e03d0f3..ac6d44af8b 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/WorldBodyBus.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Components/SimulatedBodyComponentBus.h @@ -19,44 +19,29 @@ namespace AzPhysics { - struct SimulatedBody; -} - -namespace Physics -{ - //! Requests for generic physical world bodies - class WorldBodyRequests + //! Requests for physics simulated body components. + class SimulatedBodyComponentRequests : public AZ::ComponentBus { public: using MutexType = AZStd::recursive_mutex; - //! Enable physics for this body + //! Enable physics for this body. virtual void EnablePhysics() = 0; - //! Disable physics for this body + //! Disable physics for this body. virtual void DisablePhysics() = 0; - //! Retrieve whether physics is enabled for this body + //! Retrieve whether physics is enabled for this body. virtual bool IsPhysicsEnabled() const = 0; - //! Retrieves the AABB(aligned-axis bounding box) for this body + //! Retrieves the AABB(aligned-axis bounding box) for this body. virtual AZ::Aabb GetAabb() const = 0; - //! Retrieves current WorldBody* for this body. Note: Do not hold a reference to AzPhysics::SimulatedBody* as could be deleted - virtual AzPhysics::SimulatedBody* GetWorldBody() = 0; - - //! Perform a single-object raycast against this body + //! Get the Simulated Body Handle for this body. + virtual AzPhysics::SimulatedBodyHandle GetSimulatedBodyHandle() const = 0; + //! Retrieves current WorldBody* for this body. + //! @note Do not hold a reference to AzPhysics::SimulatedBody* as it could be deleted or moved. + virtual AzPhysics::SimulatedBody* GetSimulatedBody() = 0; + //! Perform a single-object raycast against this body. virtual AzPhysics::SceneQueryHit RayCast(const AzPhysics::RayCastRequest& request) = 0; }; - using WorldBodyRequestBus = AZ::EBus; - - //! Notifications for generic physical world bodies - class WorldBodyNotifications - : public AZ::ComponentBus - { - public: - //! Notification for physics enabled - virtual void OnPhysicsEnabled() = 0; - //! Notification for physics disabled - virtual void OnPhysicsDisabled() = 0; - }; - using WorldBodyNotificationBus = AZ::EBus; + using SimulatedBodyComponentRequestsBus = AZ::EBus; } diff --git a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp index 52eae22fee..a535f5f65d 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/ShapeConfiguration.cpp @@ -48,6 +48,9 @@ namespace Physics { if (auto serializeContext = azrtti_cast(context)) { + serializeContext + ->RegisterGenericType>(); + serializeContext->Class() ->Version(1) ->Field("Radius", &SphereShapeConfiguration::m_radius) @@ -76,6 +79,9 @@ namespace Physics { if (auto serializeContext = azrtti_cast(context)) { + serializeContext + ->RegisterGenericType>(); + serializeContext->Class() ->Version(1) ->Field("Configuration", &BoxShapeConfiguration::m_dimensions) @@ -104,6 +110,9 @@ namespace Physics { if (auto serializeContext = azrtti_cast(context)) { + serializeContext + ->RegisterGenericType>(); + serializeContext->Class() ->Version(1) ->Field("Height", &CapsuleShapeConfiguration::m_height) @@ -153,6 +162,9 @@ namespace Physics { if (auto serializeContext = azrtti_cast(context)) { + serializeContext + ->RegisterGenericType>(); + serializeContext->Class() ->Version(1) ->Field("PhysicsAsset", &PhysicsAssetShapeConfiguration::m_asset) @@ -185,6 +197,9 @@ namespace Physics { if (auto serializeContext = azrtti_cast(context)) { + serializeContext + ->RegisterGenericType>(); + serializeContext->Class() ->Version(1) ->Field("Scale", &NativeShapeConfiguration::m_nativeShapeScale) @@ -208,6 +223,9 @@ namespace Physics { if (auto serializeContext = azrtti_cast(context)) { + serializeContext + ->RegisterGenericType>(); + serializeContext->Class() ->Version(1) ->Field("CookedData", &CookedMeshShapeConfiguration::m_cookedData) diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp index ae8f4308df..b5f113582b 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -39,19 +39,19 @@ namespace Physics { namespace ReflectionUtils { - void ReflectWorldBodyBus(AZ::ReflectContext* context) + void ReflectSimulatedBodyComponentRequestsBus(AZ::ReflectContext* context) { if (auto* behaviorContext = azrtti_cast(context)) { - behaviorContext->EBus("WorldBodyRequestBus") + behaviorContext->EBus("SimulatedBodyComponentRequestBus") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) ->Attribute(AZ::Script::Attributes::Module, "physics") ->Attribute(AZ::Script::Attributes::Category, "PhysX") - ->Event("EnablePhysics", &WorldBodyRequests::EnablePhysics) - ->Event("DisablePhysics", &WorldBodyRequests::DisablePhysics) - ->Event("IsPhysicsEnabled", &WorldBodyRequests::IsPhysicsEnabled) - ->Event("GetAabb", &WorldBodyRequests::GetAabb) - ->Event("RayCast", &WorldBodyRequests::RayCast) + ->Event("EnablePhysics", &AzPhysics::SimulatedBodyComponentRequests::EnablePhysics) + ->Event("DisablePhysics", &AzPhysics::SimulatedBodyComponentRequests::DisablePhysics) + ->Event("IsPhysicsEnabled", &AzPhysics::SimulatedBodyComponentRequests::IsPhysicsEnabled) + ->Event("GetAabb", &AzPhysics::SimulatedBodyComponentRequests::GetAabb) + ->Event("RayCast", &AzPhysics::SimulatedBodyComponentRequests::RayCast) ; } } @@ -131,7 +131,7 @@ namespace Physics AnimationConfiguration::Reflect(context); CharacterConfiguration::Reflect(context); AzPhysics::SimulatedBody::Reflect(context); - ReflectWorldBodyBus(context); + ReflectSimulatedBodyComponentRequestsBus(context); CollisionFilteringRequests::Reflect(context); AzPhysics::SceneQuery::ReflectSceneQueryObjects(context); ReflectWindBus(context); diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/MultiViewportController.h b/Code/Framework/AzFramework/AzFramework/Viewport/MultiViewportController.h index 2649655f50..c324104027 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/MultiViewportController.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/MultiViewportController.h @@ -48,15 +48,21 @@ namespace AzFramework }; //! The interface used by MultiViewportController to manage individual instances. + template class MultiViewportControllerInstanceInterface { public: - explicit MultiViewportControllerInstanceInterface(ViewportId viewport) + using ControllerType = TController; + + MultiViewportControllerInstanceInterface(ViewportId viewport, ControllerType* controller) : m_viewportId(viewport) + , m_controller(controller) { } ViewportId GetViewportId() const { return m_viewportId; } + ControllerType* GetController() { return m_controller; } + const ControllerType* GetController() const { return m_controller; } virtual bool HandleInputChannelEvent([[maybe_unused]]const ViewportControllerInputEvent& event) { return false; } virtual void ResetInputChannels() {} @@ -64,6 +70,7 @@ namespace AzFramework private: ViewportId m_viewportId; + ControllerType* m_controller; }; } //namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/MultiViewportController.inl b/Code/Framework/AzFramework/AzFramework/Viewport/MultiViewportController.inl index cc59418dac..4d82acdfba 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/MultiViewportController.inl +++ b/Code/Framework/AzFramework/AzFramework/Viewport/MultiViewportController.inl @@ -17,8 +17,8 @@ namespace AzFramework MultiViewportController::~MultiViewportController() { static_assert( - AZStd::is_constructible::value, - "TViewportControllerInstance must implement a TViewportControllerInstance(ViewportId) constructor" + AZStd::is_same::value, + "TViewportControllerInstance must implement a TViewportControllerInstance(ViewportId, ViewportController) constructor" ); } @@ -50,7 +50,7 @@ namespace AzFramework template void MultiViewportController::RegisterViewportContext(ViewportId viewport) { - m_instances[viewport] = AZStd::make_unique(viewport); + m_instances[viewport] = AZStd::make_unique(viewport, static_cast(this)); } template diff --git a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake index 88f68d8bab..1b1cd49aa7 100644 --- a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake +++ b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake @@ -213,6 +213,12 @@ set(FILES StreamingInstall/StreamingInstall.cpp StreamingInstall/StreamingInstallRequests.h StreamingInstall/StreamingInstallNotifications.h + Physics/Collision/CollisionEvents.h + Physics/Collision/CollisionEvents.cpp + Physics/Collision/CollisionLayers.h + Physics/Collision/CollisionLayers.cpp + Physics/Collision/CollisionGroups.h + Physics/Collision/CollisionGroups.cpp Physics/Common/PhysicsSceneQueries.h Physics/Common/PhysicsSceneQueries.cpp Physics/Common/PhysicsEvents.h @@ -223,12 +229,7 @@ set(FILES Physics/Common/PhysicsSimulatedBodyEvents.h Physics/Common/PhysicsSimulatedBodyEvents.cpp Physics/Common/PhysicsTypes.h - Physics/Collision/CollisionEvents.h - Physics/Collision/CollisionEvents.cpp - Physics/Collision/CollisionLayers.h - Physics/Collision/CollisionLayers.cpp - Physics/Collision/CollisionGroups.h - Physics/Collision/CollisionGroups.cpp + Physics/Components/SimulatedBodyComponentBus.h Physics/Configuration/CollisionConfiguration.h Physics/Configuration/CollisionConfiguration.cpp Physics/Configuration/RigidBodyConfiguration.h @@ -265,7 +266,6 @@ set(FILES Physics/ShapeConfiguration.h Physics/ShapeConfiguration.cpp Physics/SystemBus.h - Physics/WorldBodyBus.h Physics/ColliderComponentBus.h Physics/RagdollPhysicsBus.h Physics/CharacterPhysicsDataBus.h diff --git a/Code/CryEngine/CrySystem/SystemUtilsApple.h b/Code/Framework/AzFramework/Platform/Common/Apple/AzFramework/Utils/SystemUtilsApple.h similarity index 100% rename from Code/CryEngine/CrySystem/SystemUtilsApple.h rename to Code/Framework/AzFramework/Platform/Common/Apple/AzFramework/Utils/SystemUtilsApple.h diff --git a/Code/CryEngine/CrySystem/SystemUtilsApple.mm b/Code/Framework/AzFramework/Platform/Common/Apple/AzFramework/Utils/SystemUtilsApple.mm similarity index 100% rename from Code/CryEngine/CrySystem/SystemUtilsApple.mm rename to Code/Framework/AzFramework/Platform/Common/Apple/AzFramework/Utils/SystemUtilsApple.mm diff --git a/Code/Framework/AzFramework/Platform/Mac/AzFramework/Utils/SystemUtilsApple.h b/Code/Framework/AzFramework/Platform/Mac/AzFramework/Utils/SystemUtilsApple.h new file mode 100644 index 0000000000..33a89bd146 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/Mac/AzFramework/Utils/SystemUtilsApple.h @@ -0,0 +1,16 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +// Original file Copyright Crytek GMBH or its affiliates, used under license. + +#pragma once + +#include "../../../Common/Apple/AzFramework/Utils/SystemUtilsApple.h" diff --git a/Code/Framework/AzFramework/Platform/Mac/platform_mac_files.cmake b/Code/Framework/AzFramework/Platform/Mac/platform_mac_files.cmake index 9f4a09418f..b69278665a 100644 --- a/Code/Framework/AzFramework/Platform/Mac/platform_mac_files.cmake +++ b/Code/Framework/AzFramework/Platform/Mac/platform_mac_files.cmake @@ -36,4 +36,6 @@ set(FILES ../Common/Unimplemented/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard_Unimplemented.cpp AzFramework/Archive/ArchiveVars_Platform.h AzFramework/Archive/ArchiveVars_Mac.h + ../Common/Apple/AzFramework/Utils/SystemUtilsApple.h + ../Common/Apple/AzFramework/Utils/SystemUtilsApple.mm ) diff --git a/Code/Framework/AzFramework/Platform/iOS/AzFramework/Utils/SystemUtilsApple.h b/Code/Framework/AzFramework/Platform/iOS/AzFramework/Utils/SystemUtilsApple.h new file mode 100644 index 0000000000..5ac96c8523 --- /dev/null +++ b/Code/Framework/AzFramework/Platform/iOS/AzFramework/Utils/SystemUtilsApple.h @@ -0,0 +1,15 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include "../../../Common/Apple/AzFramework/Utils/SystemUtilsApple.h" diff --git a/Code/Framework/AzFramework/Platform/iOS/platform_ios_files.cmake b/Code/Framework/AzFramework/Platform/iOS/platform_ios_files.cmake index c3e5e7b7c1..f1bf958067 100644 --- a/Code/Framework/AzFramework/Platform/iOS/platform_ios_files.cmake +++ b/Code/Framework/AzFramework/Platform/iOS/platform_ios_files.cmake @@ -36,5 +36,7 @@ set(FILES AzFramework/Process/ProcessCommon.h AzFramework/Process/ProcessWatcher_iOS.cpp AzFramework/Process/ProcessCommunicator_iOS.cpp + ../Common/Apple/AzFramework/Utils/SystemUtilsApple.h + ../Common/Apple/AzFramework/Utils/SystemUtilsApple.mm ) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EntityPropertyEditorRequestsBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EntityPropertyEditorRequestsBus.h index 35b2e485b5..1959183fa0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EntityPropertyEditorRequestsBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EntityPropertyEditorRequestsBus.h @@ -31,6 +31,10 @@ namespace AzToolsFramework //! Allows a component to get the list of selected entities //! \param selectedEntityIds the return vector holding the entities required virtual void GetSelectedEntities(EntityIdList& selectedEntityIds) = 0; + + //! Explicitly sets a component as having been the most recently added. + //! This means that the next time the UI refreshes, that component will be ensured to be visible. + virtual void SetNewComponentId(AZ::ComponentId componentId) = 0; }; using EntityPropertyEditorRequestBus = AZ::EBus; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.cpp index 9caca69b34..ef6d0fe1f8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.cpp @@ -117,8 +117,6 @@ namespace AzToolsFramework { EditorEntityModel::EditorEntityModel() { - AzFramework::ApplicationRequests::Bus::BroadcastResult(m_isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled); - EntityCompositionNotificationBus::Handler::BusConnect(); EditorOnlyEntityComponentNotificationBus::Handler::BusConnect(); EditorEntityRuntimeActivationChangeNotificationBus::Handler::BusConnect(); @@ -565,7 +563,7 @@ namespace AzToolsFramework { //retrieve or add an entity entry to the table //the entry must exist, even if not connected, so children and other data can be assigned - [[maybe_unused]] auto [it, inserted] = m_entityInfoTable.try_emplace(entityId, m_isPrefabEnabled); + [[maybe_unused]] auto [it, inserted] = m_entityInfoTable.try_emplace(entityId); auto& entityInfo = it->second; //the entity id defaults to invalid and must be set to match the requested id @@ -882,11 +880,6 @@ namespace AzToolsFramework } } - EditorEntityModel::EditorEntityModelEntry::EditorEntityModelEntry(bool isPrefabEnabled) - : m_isPrefabEnabled(isPrefabEnabled) - { - } - EditorEntityModel::EditorEntityModelEntry::~EditorEntityModelEntry() { Disconnect(); @@ -1213,29 +1206,15 @@ namespace AzToolsFramework auto childItr = m_childIndexCache.find(childId); if (childItr != m_childIndexCache.end()) { - if (m_isPrefabEnabled) - { - // Take the last entry and move it into the removed spot instead of deleting the entry and having to move all - // following entries one step down. - AZ::EntityId backEntity = m_children.back(); - m_children[childItr->second] = backEntity; - // Update cached index for the moved id to the new index. - m_childIndexCache[backEntity] = childItr->second; - // Now remove the deleted id from the children and cache. - m_childIndexCache.erase(childId); - m_children.erase(m_children.end() - 1); - } - else - { - m_children.erase(m_children.begin() + childItr->second); - - // rebuild index cache for faster lookup - m_childIndexCache.clear(); - for (auto childIdToCache : m_children) - { - m_childIndexCache[childIdToCache] = static_cast(m_childIndexCache.size()); - } - } + // Take the last entry and move it into the removed spot instead of deleting the entry and having to move all + // following entries one step down. + AZ::EntityId backEntity = m_children.back(); + m_children[childItr->second] = backEntity; + // Update cached index for the moved id to the new index. + m_childIndexCache[backEntity] = childItr->second; + // Now remove the deleted id from the children and cache. + m_childIndexCache.erase(childId); + m_children.erase(m_children.end() - 1); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.h index b6cceb85fe..72965b4017 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityModel.h @@ -171,7 +171,6 @@ namespace AzToolsFramework , public PropertyEditorEntityChangeNotificationBus::Handler { public: - explicit EditorEntityModelEntry(bool isPrefabEnabled); ~EditorEntityModelEntry(); // Separately connect to EditorEntityInfoRequestBus and refresh Entity @@ -336,7 +335,6 @@ namespace AzToolsFramework bool m_visible = true; bool m_locked = false; bool m_connected = false; - bool m_isPrefabEnabled = false; AZStd::string m_name; AZStd::string m_sliceAssetName; AZStd::unordered_map m_childIndexCache; @@ -375,6 +373,5 @@ namespace AzToolsFramework AZ::EntityId m_postInstantiateBeforeEntity; AZ::EntityId m_postInstantiateSliceParent; bool m_gotInstantiateSliceDetails = false; - bool m_isPrefabEnabled = false; }; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 475510c52f..d4485f8e99 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -182,6 +182,22 @@ namespace AzToolsFramework instanceToParentUnder = prefabEditorEntityOwnershipInterface->GetRootPrefabInstance(); parent = instanceToParentUnder->get().GetContainerEntityId(); } + + //Detect whether this instantiation would produce a cyclical dependency + auto relativePath = m_prefabLoaderInterface->GetRelativePathToProject(filePath); + Prefab::TemplateId templateId = m_prefabSystemComponentInterface->GetTemplateIdFromFilePath(relativePath); + + // If the template isn't currently loaded, there's no way for it to be in the hierarchy so we just skip the check. + if (templateId != Prefab::InvalidTemplateId && IsPrefabInInstanceAncestorHierarchy(templateId, instanceToParentUnder->get())) + { + return AZ::Failure( + AZStd::string::format( + "Instantiate Prefab operation aborted - Cyclical dependency detected\n(%s depends on %s).", + relativePath.Native().c_str(), + instanceToParentUnder->get().GetTemplateSourcePath().Native().c_str() + ) + ); + } { // Initialize Undo Batch object @@ -192,7 +208,7 @@ namespace AzToolsFramework instanceToParentUnderDomBeforeCreate, instanceToParentUnder->get()); // Instantiate the Prefab - auto instanceToCreate = prefabEditorEntityOwnershipInterface->InstantiatePrefab(filePath, instanceToParentUnder); + auto instanceToCreate = prefabEditorEntityOwnershipInterface->InstantiatePrefab(relativePath, instanceToParentUnder); if (!instanceToCreate) { @@ -242,6 +258,23 @@ namespace AzToolsFramework return AZ::Success(); } + bool PrefabPublicHandler::IsPrefabInInstanceAncestorHierarchy(TemplateId prefabTemplateId, InstanceOptionalConstReference instance) + { + InstanceOptionalConstReference currentInstance = instance; + + while (currentInstance.has_value()) + { + if (currentInstance->get().GetTemplateId() == prefabTemplateId) + { + return true; + } + + currentInstance = currentInstance->get().GetParentInstance(); + } + + return false; + } + void PrefabPublicHandler::CreateLink( const EntityList& topLevelEntities, Instance& sourceInstance, TemplateId targetTemplateId, UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 5ade666a40..03b3827328 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -106,6 +106,14 @@ namespace AzToolsFramework const AZStd::vector& entityIds, EntityList& inputEntityList, EntityList& topLevelEntities, AZ::EntityId& commonRootEntityId, InstanceOptionalReference& commonRootEntityOwningInstance); + /* Detects whether an instance of prefabTemplateId is present in the hierarchy of ancestors of instance. + * + * \param prefabTemplateId The template id to test for + * \param instance The instance whose ancestor hierarchy prefabTemplateId will be tested against. + * \return true if an instance of the template of id prefabTemplateId could be found in the ancestor hierarchy of instance, false otherwise. + */ + bool IsPrefabInInstanceAncestorHierarchy(TemplateId prefabTemplateId, InstanceOptionalConstReference instance); + static Instance* GetParentInstance(Instance* instance); static Instance* GetAncestorOfInstanceThatIsChildOfRoot(const Instance* ancestor, Instance* descendant); static void GenerateContainerEntityTransform(const EntityList& topLevelEntities, AZ::Vector3& translation, AZ::Quaternion& rotation); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index 4413586e77..3910e80f75 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -721,6 +721,8 @@ namespace AzToolsFramework TemplateId PrefabSystemComponent::GetTemplateIdFromFilePath(AZ::IO::PathView filePath) const { + AZ_Assert(!filePath.IsAbsolute(), "Prefab - GetTemplateIdFromFilePath was passed an absolute path. Prefabs use paths relative to the project folder."); + auto found = m_templateFilePathToIdMap.find(filePath); if (found != m_templateFilePathToIdMap.end()) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.cpp index 5e928a382a..989398f196 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.cpp @@ -39,9 +39,10 @@ namespace AzToolsFramework editContext->Class("Non-uniform Scale", "Non-uniform scale for this entity only (does not propagate through hierarchy)") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::Category, "Non-uniform Scale") - ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::FixedComponentListIndex, 1) + ->Attribute(AZ::Edit::Attributes::RemoveableByUser, true) + ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/NonUniformScale.svg") + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/NonUniformScale.svg") ->DataElement( AZ::Edit::UIHandlers::Default, &EditorNonUniformScaleComponent::m_scale, "Non-uniform Scale", "Non-uniform scale for this entity only (does not propagate through hierarchy)") @@ -61,6 +62,8 @@ namespace AzToolsFramework void EditorNonUniformScaleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { + incompatible.push_back(AZ_CRC_CE("NonUniformScaleService")); + incompatible.push_back(AZ_CRC_CE("DebugDrawObbService")); incompatible.push_back(AZ_CRC_CE("DebugDrawService")); incompatible.push_back(AZ_CRC_CE("EMotionFXActorService")); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp index dcced5b705..f8d02b6581 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp @@ -25,11 +25,15 @@ #include #include #include +#include +#include #include #include #include #include #include +#include +#include #include #include @@ -1196,6 +1200,66 @@ namespace AzToolsFramework destinationComponent->SetWorldTM(const_cast(sourceComponent)->GetWorldTM()); } + AZ::Component* TransformComponent::FindPresentOrPendingComponent(AZ::Uuid componentUuid) + { + // first check if the component is present and valid + if (AZ::Component* foundComponent = GetEntity()->FindComponent(componentUuid)) + { + return foundComponent; + } + + // then check to see if there's a component pending because it's in an invalid state + AZStd::vector pendingComponents; + AzToolsFramework::EditorPendingCompositionRequestBus::Event(GetEntityId(), + &AzToolsFramework::EditorPendingCompositionRequests::GetPendingComponents, pendingComponents); + + for (const auto pendingComponent : pendingComponents) + { + if (pendingComponent->RTTI_IsTypeOf(componentUuid)) + { + return pendingComponent; + } + } + + return nullptr; + } + + bool TransformComponent::IsAddNonUniformScaleButtonReadOnly() + { + return FindPresentOrPendingComponent(EditorNonUniformScaleComponent::TYPEINFO_Uuid()) != nullptr; + } + + AZ::Crc32 TransformComponent::OnAddNonUniformScaleButtonPressed() + { + // if there is already a non-uniform scale component, do nothing + if (FindPresentOrPendingComponent(EditorNonUniformScaleComponent::TYPEINFO_Uuid())) + { + return AZ::Edit::PropertyRefreshLevels::None; + } + + const AZStd::vector entityList = { GetEntityId() }; + const AZ::ComponentTypeList componentsToAdd = { EditorNonUniformScaleComponent::TYPEINFO_Uuid() }; + + AzToolsFramework::EntityCompositionRequests::AddComponentsOutcome addComponentsOutcome; + AzToolsFramework::EntityCompositionRequestBus::BroadcastResult(addComponentsOutcome, + &AzToolsFramework::EntityCompositionRequests::AddComponentsToEntities, entityList, componentsToAdd); + + const auto nonUniformScaleComponent = FindPresentOrPendingComponent(EditorNonUniformScaleComponent::RTTI_Type()); + AZ::ComponentId nonUniformScaleComponentId = + nonUniformScaleComponent ? nonUniformScaleComponent->GetId() : AZ::InvalidComponentId; + + if (!addComponentsOutcome.IsSuccess() || !nonUniformScaleComponent) + { + AZ_Warning("Transform component", false, "Failed to add non-uniform scale component."); + return AZ::Edit::PropertyRefreshLevels::None; + } + + AzToolsFramework::EntityPropertyEditorRequestBus::Broadcast( + &AzToolsFramework::EntityPropertyEditorRequests::SetNewComponentId, nonUniformScaleComponentId); + + return AZ::Edit::PropertyRefreshLevels::EntireTree; + } + void TransformComponent::Reflect(AZ::ReflectContext* context) { // reflect data for script, serialization, editing.. @@ -1211,6 +1275,7 @@ namespace AzToolsFramework serializeContext->Class()-> Field("Parent Entity", &TransformComponent::m_parentEntityId)-> Field("Transform Data", &TransformComponent::m_editorTransform)-> + Field("AddNonUniformScaleButton", &TransformComponent::m_addNonUniformScaleButton)-> Field("Cached World Transform", &TransformComponent::m_cachedWorldTransform)-> Field("Cached World Transform Parent", &TransformComponent::m_cachedWorldTransformParent)-> Field("Parent Activation Transform Mode", &TransformComponent::m_parentActivationTransformMode)-> @@ -1224,6 +1289,7 @@ namespace AzToolsFramework { ptrEdit->Class("Transform", "Controls the placement of the entity in the world in 3d")-> ClassElement(AZ::Edit::ClassElements::EditorData, "")-> + Attribute(AZ::Edit::Attributes::FixedComponentListIndex, 0)-> Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Transform.svg")-> Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Transform.png")-> Attribute(AZ::Edit::Attributes::AutoExpand, true)-> @@ -1234,6 +1300,10 @@ namespace AzToolsFramework DataElement(AZ::Edit::UIHandlers::Default, &TransformComponent::m_editorTransform, "Values", "")-> Attribute(AZ::Edit::Attributes::ChangeNotify, &TransformComponent::TransformChanged)-> Attribute(AZ::Edit::Attributes::AutoExpand, true)-> + DataElement(AZ::Edit::UIHandlers::Button, &TransformComponent::m_addNonUniformScaleButton, "", "")-> + Attribute(AZ::Edit::Attributes::ButtonText, "Add non-uniform scale")-> + Attribute(AZ::Edit::Attributes::ReadOnly, &TransformComponent::IsAddNonUniformScaleButtonReadOnly)-> + Attribute(AZ::Edit::Attributes::ChangeNotify, &TransformComponent::OnAddNonUniformScaleButtonPressed)-> DataElement(AZ::Edit::UIHandlers::ComboBox, &TransformComponent::m_parentActivationTransformMode, "Parent activation", "Configures relative transform behavior when parent activates.")-> EnumAttribute(AZ::TransformConfig::ParentActivationTransformMode::MaintainOriginalRelativeTransform, "Original relative transform")-> diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.h index 8327c5f128..3d1e1ed672 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "EditorComponentBase.h" #include "TransformComponentBus.h" @@ -228,6 +229,10 @@ namespace AzToolsFramework void CheckApplyCachedWorldTransform(const AZ::Transform& parentWorld); + AZ::Component* FindPresentOrPendingComponent(AZ::Uuid componentUuid); + bool IsAddNonUniformScaleButtonReadOnly(); + AZ::Crc32 OnAddNonUniformScaleButtonPressed(); + // Drives transform behavior when parent activates. See AZ::TransformConfig::ParentActivationTransformMode for details. AZ::TransformConfig::ParentActivationTransformMode m_parentActivationTransformMode; @@ -260,6 +265,10 @@ namespace AzToolsFramework bool m_worldTransformDirty = true; bool m_isStatic = false; + // This is a workaround for a bug which causes the button to appear with incorrect placement if a UI + // element is used rather than a data element. + bool m_addNonUniformScaleButton = false; + // Deprecated AZ::InterpolationMode m_interpolatePosition; AZ::InterpolationMode m_interpolateRotation; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp index 181f5b9a9d..bfb2ff4256 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp @@ -63,6 +63,7 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include #include #include #include @@ -494,6 +495,11 @@ namespace AzToolsFramework } } + void EntityPropertyEditor::SetNewComponentId(AZ::ComponentId componentId) + { + m_newComponentId = componentId; + } + void EntityPropertyEditor::SetOverrideEntityIds(const AzToolsFramework::EntityIdSet& entities) { m_overrideSelectedEntityIds = entities; @@ -1039,15 +1045,23 @@ namespace AzToolsFramework sortedComponents.end(), [=](const OrderedSortComponentEntry& component1, const OrderedSortComponentEntry& component2) { - // Transform component must be first, always - // If component 1 is a transform component, it is sorted earlier - if (component1.m_component->RTTI_IsTypeOf(AZ::EditorTransformComponentTypeId)) + AZStd::optional fixedComponentListIndex1 = GetFixedComponentListIndex(component1.m_component); + AZStd::optional fixedComponentListIndex2 = GetFixedComponentListIndex(component2.m_component); + + // If both components have fixed list indices, sort based on those indices + if (fixedComponentListIndex1.has_value() && fixedComponentListIndex2.has_value()) + { + return fixedComponentListIndex1.value() < fixedComponentListIndex2.value(); + } + + // If component 1 has a fixed list index, sort it first + if (fixedComponentListIndex1.has_value()) { return true; } - // If component 2 is a transform component, component 1 is never sorted earlier - if (component2.m_component->RTTI_IsTypeOf(AZ::EditorTransformComponentTypeId)) + // If component 2 has a fixed list index, component 1 should not be sorted before it + if (fixedComponentListIndex2.has_value()) { return false; } @@ -1128,10 +1142,7 @@ namespace AzToolsFramework { if (auto attributeData = azdynamic_cast*>(attribute)) { - if (!attributeData->Get(nullptr)) - { - return false; - } + return attributeData->Get(nullptr); } } } @@ -1166,6 +1177,36 @@ namespace AzToolsFramework return true; } + AZStd::optional EntityPropertyEditor::GetFixedComponentListIndex(const AZ::Component* component) + { + auto componentClassData = component ? GetComponentClassData(component) : nullptr; + if (componentClassData && componentClassData->m_editData) + { + if (auto editorDataElement = componentClassData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData)) + { + if (auto attribute = editorDataElement->FindAttribute(AZ::Edit::Attributes::FixedComponentListIndex)) + { + if (auto attributeData = azdynamic_cast*>(attribute)) + { + return { attributeData->Get(nullptr) }; + } + } + } + } + return {}; + } + + bool EntityPropertyEditor::IsComponentDraggable(const AZ::Component* component) + { + return !GetFixedComponentListIndex(component).has_value(); + } + + bool EntityPropertyEditor::AreComponentsDraggable(const AZ::Entity::ComponentArrayType& components) const + { + return AZStd::all_of( + components.begin(), components.end(), [](AZ::Component* component) { return IsComponentDraggable(component); }); + } + bool EntityPropertyEditor::AreComponentsCopyable(const AZ::Entity::ComponentArrayType& components) const { return AreComponentsCopyable(components, m_componentFilter); @@ -3367,7 +3408,9 @@ namespace AzToolsFramework sourceComponents.size() == m_selectedEntityIds.size() && targetComponents.size() == m_selectedEntityIds.size() && AreComponentsRemovable(sourceComponents) && - AreComponentsRemovable(targetComponents); + AreComponentsRemovable(targetComponents) && + AreComponentsDraggable(sourceComponents) && + AreComponentsDraggable(targetComponents); } bool EntityPropertyEditor::IsMoveComponentsUpAllowed() const @@ -3681,14 +3724,38 @@ namespace AzToolsFramework void EntityPropertyEditor::ScrollToNewComponent() { - //force new components to be visible, assuming they are added to the end of the list and layout - auto componentEditor = GetComponentEditorsFromIndex(m_componentEditorsUsed - 1); + // force new components to be visible + // if no component has been explicitly set at the most recently added, + // assume new components are added to the end of the list and layout + AZ::s32 newComponentIndex = m_componentEditorsUsed - 1; + + // if there is a component id explicitly set as the most recently added, try to find it and make sure it is visible + if (m_newComponentId.has_value() && m_newComponentId.value() != AZ::InvalidComponentId) + { + AZ::ComponentId newComponentId = m_newComponentId.value(); + for (AZ::s32 componentIndex = 0; componentIndex < m_componentEditorsUsed; ++componentIndex) + { + if (m_componentEditors[componentIndex]) + { + for (const auto component : m_componentEditors[componentIndex]->GetComponents()) + { + if (component->GetId() == newComponentId) + { + newComponentIndex = componentIndex; + } + } + } + } + } + + auto componentEditor = GetComponentEditorsFromIndex(newComponentIndex); if (componentEditor) { m_gui->m_componentList->ensureWidgetVisible(componentEditor); } m_shouldScrollToNewComponents = false; m_shouldScrollToNewComponentsQueued = false; + m_newComponentId.reset(); } void EntityPropertyEditor::QueueScrollToNewComponent() @@ -4073,7 +4140,8 @@ namespace AzToolsFramework { if (!componentEditor || !componentEditor->isVisible() || - !AreComponentsRemovable(componentEditor->GetComponents())) + !AreComponentsRemovable(componentEditor->GetComponents()) || + !AreComponentsDraggable(componentEditor->GetComponents())) { return false; } @@ -4223,6 +4291,7 @@ namespace AzToolsFramework while (targetComponentEditor && (targetComponentEditor->IsDragged() || !AreComponentsRemovable(targetComponentEditor->GetComponents()) + || !AreComponentsDraggable(targetComponentEditor->GetComponents()) || (globalRect.center().y() > GetWidgetGlobalRect(targetComponentEditor).center().y()))) { if (targetItr == m_componentEditors.end() || targetComponentEditor == m_componentEditors.back() || !targetComponentEditor->isVisible()) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx index 677dc98277..9cd380ae06 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx @@ -211,6 +211,7 @@ namespace AzToolsFramework // EntityPropertEditorRequestBus void GetSelectedAndPinnedEntities(EntityIdList& selectedEntityIds) override; void GetSelectedEntities(EntityIdList& selectedEntityIds) override; + void SetNewComponentId(AZ::ComponentId componentId) override; bool IsEntitySelected(const AZ::EntityId& id) const; bool IsSingleEntitySelected(const AZ::EntityId& id) const; @@ -237,6 +238,9 @@ namespace AzToolsFramework static bool DoesComponentPassFilter(const AZ::Component* component, const ComponentFilter& filter); static bool IsComponentRemovable(const AZ::Component* component); bool AreComponentsRemovable(const AZ::Entity::ComponentArrayType& components) const; + static AZStd::optional GetFixedComponentListIndex(const AZ::Component* component); + static bool IsComponentDraggable(const AZ::Component* component); + bool AreComponentsDraggable(const AZ::Entity::ComponentArrayType& components) const; bool AreComponentsCopyable(const AZ::Entity::ComponentArrayType& components) const; void AddMenuOptionsForComponents(QMenu& menu, const QPoint& position); @@ -568,6 +572,9 @@ namespace AzToolsFramework void ConnectToEntityBuses(const AZ::EntityId& entityId); void DisconnectFromEntityBuses(const AZ::EntityId& entityId); + //! Stores a component id to be focused on next time the UI updates. + AZStd::optional m_newComponentId; + private slots: void OnPropertyRefreshRequired(); // refresh is needed for a property. void UpdateContents(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h index 6ceb175fe4..ee95412376 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h @@ -178,6 +178,24 @@ namespace AzToolsFramework ~ViewportInteractionRequests() = default; }; + /// Interface to return only viewport specific settings (e.g. snapping). + class ViewportSettings + { + public: + virtual ~ViewportSettings() = default; + + /// Return if grid snapping is enabled. + virtual bool GridSnappingEnabled() const = 0; + /// Return the grid snapping size. + virtual float GridSize() const = 0; + /// Does the grid currently want to be displayed. + virtual bool ShowGrid() const = 0; + /// Return if angle snapping is enabled. + virtual bool AngleSnappingEnabled() const = 0; + /// Return the angle snapping/step size. + virtual float AngleStep() const = 0; + }; + /// Type to inherit to implement ViewportInteractionRequests. using ViewportInteractionRequestBus = AZ::EBus; @@ -244,6 +262,8 @@ namespace AzToolsFramework /// from ViewportCursorScreenPosition. This method will always return the correct position to generate a mouse /// position delta. virtual AZStd::optional PreviousViewportCursorScreenPosition() = 0; + /// Is mouse over viewport. + virtual bool IsMouseOver() const = 0; protected: ~ViewportMouseCursorRequests() = default; diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabUpdateInstancesBenchmarks.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabUpdateInstancesBenchmarks.cpp index c81db58c95..d1bd2a488e 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabUpdateInstancesBenchmarks.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabUpdateInstancesBenchmarks.cpp @@ -78,7 +78,7 @@ namespace Benchmark } BENCHMARK_REGISTER_F(BM_PrefabUpdateInstances, UpdateInstances_SingeEntityInstances) ->RangeMultiplier(10) - ->Range(100, 1000) + ->Range(100, 10000) ->Unit(benchmark::kMillisecond) ->Complexity(); diff --git a/Code/Framework/AzToolsFramework/Tests/UI/EntityPropertyEditorTests.cpp b/Code/Framework/AzToolsFramework/Tests/UI/EntityPropertyEditorTests.cpp index 3017df737d..d0f93bfdf8 100644 --- a/Code/Framework/AzToolsFramework/Tests/UI/EntityPropertyEditorTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/UI/EntityPropertyEditorTests.cpp @@ -17,13 +17,13 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include @@ -55,7 +55,7 @@ namespace UnitTest TEST(EntityPropertyEditorTests, PrioritySort_NonTransformAsFirstItem_TransformMovesToTopRemainderUnchanged) { - ComponentApplication app; + ToolsApplication app; AZ::Entity::ComponentArrayType unorderedComponents; AZ::Entity::ComponentArrayType orderedComponents; @@ -68,12 +68,18 @@ namespace UnitTest Entity* systemEntity = app.Create(desc, startupParams); + // Need to reflect the components so that edit attribute used for sorting, such as FixedComponentListIndex, get set. + app.RegisterComponentDescriptor(AzToolsFramework::Components::TransformComponent::CreateDescriptor()); + app.RegisterComponentDescriptor(AzToolsFramework::Components::ScriptEditorComponent::CreateDescriptor()); + app.RegisterComponentDescriptor(AZ::AssetManagerComponent::CreateDescriptor()); + // Add more than 31 components, as we are testing the case where the sort fails when there are 32 or more items. const int numFillerItems = 32; for (int commentIndex = 0; commentIndex < numFillerItems; commentIndex++) { - unorderedComponents.insert(unorderedComponents.begin(), systemEntity->CreateComponent(AZ::StreamerComponent::RTTI_Type())); + unorderedComponents.insert(unorderedComponents.begin(), systemEntity->CreateComponent( + AzToolsFramework::Components::ScriptEditorComponent::RTTI_Type())); } // Add a TransformComponent at the end which should be sorted to the beginning by the priority sort. diff --git a/Code/LauncherUnified/Platform/iOS/O3DEApplicationDelegate_iOS.mm b/Code/LauncherUnified/Platform/iOS/O3DEApplicationDelegate_iOS.mm index ca7ab2def0..d78a83607b 100644 --- a/Code/LauncherUnified/Platform/iOS/O3DEApplicationDelegate_iOS.mm +++ b/Code/LauncherUnified/Platform/iOS/O3DEApplicationDelegate_iOS.mm @@ -19,7 +19,7 @@ #include // for AZ_MAX_PATH_LEN -#include +#include #import diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index d9344746a2..0b3ff87e43 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -106,6 +106,15 @@ AZ_CVAR( EditorViewportWidget* EditorViewportWidget::m_pPrimaryViewport = nullptr; +namespace AzFramework +{ + extern InputChannelId CameraFreeLookButton; + extern InputChannelId CameraFreePanButton; + extern InputChannelId CameraOrbitLookButton; + extern InputChannelId CameraOrbitDollyButton; + extern InputChannelId CameraOrbitPanButton; +} + #if AZ_TRAIT_OS_PLATFORM_APPLE void StopFixedCursorMode(); void StartFixedCursorMode(QObject *viewport); @@ -161,6 +170,7 @@ EditorViewportWidget::EditorViewportWidget(const QString& name, QWidget* parent) , m_camFOV(gSettings.viewports.fDefaultFov) , m_defaultViewName(name) , m_renderViewport(nullptr) //m_renderViewport is initialized later, in SetViewportId + , m_editorViewportSettings(this) { // need this to be set in order to allow for language switching on Windows setAttribute(Qt::WA_InputMethodEnabled); @@ -1098,32 +1108,6 @@ AzFramework::CameraState EditorViewportWidget::GetCameraState() return m_renderViewport->GetCameraState(); } -bool EditorViewportWidget::GridSnappingEnabled() -{ - return GetViewManager()->GetGrid()->IsEnabled(); -} - -float EditorViewportWidget::GridSize() -{ - const CGrid* grid = GetViewManager()->GetGrid(); - return grid->scale * grid->size; -} - -bool EditorViewportWidget::ShowGrid() -{ - return gSettings.viewports.bShowGridGuide; -} - -bool EditorViewportWidget::AngleSnappingEnabled() -{ - return GetViewManager()->GetGrid()->IsAngleSnapEnabled(); -} - -float EditorViewportWidget::AngleStep() -{ - return GetViewManager()->GetGrid()->GetAngleSnap(); -} - AZ::Vector3 EditorViewportWidget::PickTerrain(const AzFramework::ScreenPoint& point) { FUNCTION_PROFILER(GetIEditor()->GetSystem(), PROFILE_EDITOR); @@ -1227,13 +1211,47 @@ void EditorViewportWidget::SetViewportId(int id) if (ed_useNewCameraSystem) { AzFramework::ReloadCameraKeyBindings(); - m_renderViewport->GetControllerList()->Add(AZStd::make_shared()); + + auto controller = AZStd::make_shared(); + controller->SetCameraListBuilderCallback([](AzFramework::Cameras& cameras) + { + auto firstPersonRotateCamera = AZStd::make_shared(AzFramework::CameraFreeLookButton); + auto firstPersonPanCamera = + AZStd::make_shared(AzFramework::CameraFreePanButton, AzFramework::LookPan); + auto firstPersonTranslateCamera = AZStd::make_shared(AzFramework::LookTranslation); + auto firstPersonWheelCamera = AZStd::make_shared(); + + auto orbitCamera = AZStd::make_shared(); + auto orbitRotateCamera = AZStd::make_shared(AzFramework::CameraOrbitLookButton); + auto orbitTranslateCamera = AZStd::make_shared(AzFramework::OrbitTranslation); + auto orbitDollyWheelCamera = AZStd::make_shared(); + auto orbitDollyMoveCamera = + AZStd::make_shared(AzFramework::CameraOrbitDollyButton); + auto orbitPanCamera = + AZStd::make_shared(AzFramework::CameraOrbitPanButton, AzFramework::OrbitPan); + + orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera); + orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera); + orbitCamera->m_orbitCameras.AddCamera(orbitDollyWheelCamera); + orbitCamera->m_orbitCameras.AddCamera(orbitDollyMoveCamera); + orbitCamera->m_orbitCameras.AddCamera(orbitPanCamera); + + cameras.AddCamera(firstPersonRotateCamera); + cameras.AddCamera(firstPersonPanCamera); + cameras.AddCamera(firstPersonTranslateCamera); + cameras.AddCamera(firstPersonWheelCamera); + cameras.AddCamera(orbitCamera); + }); + + m_renderViewport->GetControllerList()->Add(controller); } else { m_renderViewport->GetControllerList()->Add(AZStd::make_shared()); } + m_renderViewport->SetViewportSettings(&m_editorViewportSettings); + UpdateScene(); if (m_pPrimaryViewport == this) @@ -2853,4 +2871,35 @@ void EditorViewportWidget::SetAsActiveViewport() } } +EditorViewportSettings::EditorViewportSettings(const EditorViewportWidget* editorViewportWidget) + : m_editorViewportWidget(editorViewportWidget) +{ +} + +bool EditorViewportSettings::GridSnappingEnabled() const +{ + return m_editorViewportWidget->GetViewManager()->GetGrid()->IsEnabled(); +} + +float EditorViewportSettings::GridSize() const +{ + const CGrid* grid = m_editorViewportWidget->GetViewManager()->GetGrid(); + return grid->scale * grid->size; +} + +bool EditorViewportSettings::ShowGrid() const +{ + return gSettings.viewports.bShowGridGuide; +} + +bool EditorViewportSettings::AngleSnappingEnabled() const +{ + return m_editorViewportWidget->GetViewManager()->GetGrid()->IsAngleSnapEnabled(); +} + +float EditorViewportSettings::AngleStep() const +{ + return m_editorViewportWidget->GetViewManager()->GetGrid()->GetAngleSnap(); +} + #include diff --git a/Code/Sandbox/Editor/EditorViewportWidget.h b/Code/Sandbox/Editor/EditorViewportWidget.h index 09474200f1..8675c035f6 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.h +++ b/Code/Sandbox/Editor/EditorViewportWidget.h @@ -65,6 +65,23 @@ namespace AzToolsFramework class ManipulatorManager; } +class EditorViewportWidget; + +//! Viewport settings for the EditorViewportWidget +struct EditorViewportSettings : public AzToolsFramework::ViewportInteraction::ViewportSettings +{ + explicit EditorViewportSettings(const EditorViewportWidget* editorViewportWidget); + + bool GridSnappingEnabled() const override; + float GridSize() const override; + bool ShowGrid() const override; + bool AngleSnappingEnabled() const override; + float AngleStep() const override; + +private: + const EditorViewportWidget* m_editorViewportWidget = nullptr; +}; + // EditorViewportWidget window AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING @@ -189,13 +206,7 @@ public: virtual void OnStartPlayInEditor(); virtual void OnStopPlayInEditor(); - // AzToolsFramework::ViewportInteractionRequestBus AzFramework::CameraState GetCameraState(); - bool GridSnappingEnabled(); - float GridSize(); - bool ShowGrid(); - bool AngleSnappingEnabled(); - float AngleStep(); AzFramework::ScreenPoint ViewportWorldToScreen(const AZ::Vector3& worldPosition); // AzToolsFramework::ViewportFreezeRequestBus @@ -596,5 +607,7 @@ private: AZ::Name m_defaultViewportContextName; + EditorViewportSettings m_editorViewportSettings; + AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING }; diff --git a/Code/Sandbox/Editor/LegacyViewportCameraController.cpp b/Code/Sandbox/Editor/LegacyViewportCameraController.cpp index 518b17f898..56bf986cfc 100644 --- a/Code/Sandbox/Editor/LegacyViewportCameraController.cpp +++ b/Code/Sandbox/Editor/LegacyViewportCameraController.cpp @@ -28,8 +28,8 @@ namespace SandboxEditor { -LegacyViewportCameraControllerInstance::LegacyViewportCameraControllerInstance(AzFramework::ViewportId viewportId) - : AzFramework::MultiViewportControllerInstanceInterface(viewportId) +LegacyViewportCameraControllerInstance::LegacyViewportCameraControllerInstance(AzFramework::ViewportId viewportId, LegacyViewportCameraController* controller) + : AzFramework::MultiViewportControllerInstanceInterface(viewportId, controller) { } diff --git a/Code/Sandbox/Editor/LegacyViewportCameraController.h b/Code/Sandbox/Editor/LegacyViewportCameraController.h index 129a2409da..84a8fe2301 100644 --- a/Code/Sandbox/Editor/LegacyViewportCameraController.h +++ b/Code/Sandbox/Editor/LegacyViewportCameraController.h @@ -28,11 +28,14 @@ namespace AzFramework namespace SandboxEditor { + class LegacyViewportCameraControllerInstance; + using LegacyViewportCameraController = AzFramework::MultiViewportController; + class LegacyViewportCameraControllerInstance final - : public AzFramework::MultiViewportControllerInstanceInterface + : public AzFramework::MultiViewportControllerInstanceInterface { public: - explicit LegacyViewportCameraControllerInstance(AzFramework::ViewportId viewport); + LegacyViewportCameraControllerInstance(AzFramework::ViewportId viewport, LegacyViewportCameraController* controller); bool HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) override; void ResetInputChannels() override; @@ -69,5 +72,4 @@ namespace SandboxEditor bool m_capturingCursor = false; }; - using LegacyViewportCameraController = AzFramework::MultiViewportController; } //namespace SandboxEditor diff --git a/Code/Sandbox/Editor/MainWindow.cpp b/Code/Sandbox/Editor/MainWindow.cpp index 895df4214f..e9bfd96a1d 100644 --- a/Code/Sandbox/Editor/MainWindow.cpp +++ b/Code/Sandbox/Editor/MainWindow.cpp @@ -1327,7 +1327,7 @@ QToolButton* MainWindow::CreateDebugModeButton() QWidget* MainWindow::CreateSpacerRightWidget() { - QWidget* spacer = new QWidget(); + QWidget* spacer = new QWidget(this); spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); spacer->setVisible(true); return spacer; diff --git a/Code/Sandbox/Editor/ModernViewportCameraController.cpp b/Code/Sandbox/Editor/ModernViewportCameraController.cpp index 4725721f0e..1c2771514f 100644 --- a/Code/Sandbox/Editor/ModernViewportCameraController.cpp +++ b/Code/Sandbox/Editor/ModernViewportCameraController.cpp @@ -22,15 +22,6 @@ #include #include -namespace AzFramework -{ - extern InputChannelId CameraFreeLookButton; - extern InputChannelId CameraFreePanButton; - extern InputChannelId CameraOrbitLookButton; - extern InputChannelId CameraOrbitDollyButton; - extern InputChannelId CameraOrbitPanButton; -} - namespace SandboxEditor { static void DrawPreviewAxis(AzFramework::DebugDisplayRequests& display, const AZ::Transform& transform, const float axisLength) @@ -60,36 +51,23 @@ namespace SandboxEditor return viewportContext; } - ModernViewportCameraControllerInstance::ModernViewportCameraControllerInstance(const AzFramework::ViewportId viewportId) - : MultiViewportControllerInstanceInterface(viewportId) + void ModernViewportCameraController::SetCameraListBuilderCallback(const CameraListBuilder& builder) { - // LYN-2315 TODO - move setup out of constructor, pass cameras in - auto firstPersonRotateCamera = AZStd::make_shared(AzFramework::CameraFreeLookButton); - auto firstPersonPanCamera = - AZStd::make_shared(AzFramework::CameraFreePanButton, AzFramework::LookPan); - auto firstPersonTranslateCamera = AZStd::make_shared(AzFramework::LookTranslation); - auto firstPersonWheelCamera = AZStd::make_shared(); + m_cameraListBuilder = builder; + } - auto orbitCamera = AZStd::make_shared(); - auto orbitRotateCamera = AZStd::make_shared(AzFramework::CameraOrbitLookButton); - auto orbitTranslateCamera = AZStd::make_shared(AzFramework::OrbitTranslation); - auto orbitDollyWheelCamera = AZStd::make_shared(); - auto orbitDollyMoveCamera = - AZStd::make_shared(AzFramework::CameraOrbitDollyButton); - auto orbitPanCamera = - AZStd::make_shared(AzFramework::CameraOrbitPanButton, AzFramework::OrbitPan); + void ModernViewportCameraController::SetupCameras(AzFramework::Cameras& cameras) + { + if (m_cameraListBuilder) + { + m_cameraListBuilder(cameras); + } + } - orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera); - orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera); - orbitCamera->m_orbitCameras.AddCamera(orbitDollyWheelCamera); - orbitCamera->m_orbitCameras.AddCamera(orbitDollyMoveCamera); - orbitCamera->m_orbitCameras.AddCamera(orbitPanCamera); - - m_cameraSystem.m_cameras.AddCamera(firstPersonRotateCamera); - m_cameraSystem.m_cameras.AddCamera(firstPersonPanCamera); - m_cameraSystem.m_cameras.AddCamera(firstPersonTranslateCamera); - m_cameraSystem.m_cameras.AddCamera(firstPersonWheelCamera); - m_cameraSystem.m_cameras.AddCamera(orbitCamera); + ModernViewportCameraControllerInstance::ModernViewportCameraControllerInstance(const AzFramework::ViewportId viewportId, ModernViewportCameraController* controller) + : MultiViewportControllerInstanceInterface(viewportId, controller) + { + controller->SetupCameras(m_cameraSystem.m_cameras); if (auto viewportContext = RetrieveViewportContext(GetViewportId())) { diff --git a/Code/Sandbox/Editor/ModernViewportCameraController.h b/Code/Sandbox/Editor/ModernViewportCameraController.h index 56345ad0da..b1ff8d1039 100644 --- a/Code/Sandbox/Editor/ModernViewportCameraController.h +++ b/Code/Sandbox/Editor/ModernViewportCameraController.h @@ -19,12 +19,29 @@ namespace SandboxEditor { - class ModernViewportCameraControllerInstance final : public AzFramework::MultiViewportControllerInstanceInterface, - private AzFramework::ViewportDebugDisplayEventBus::Handler + class ModernViewportCameraControllerInstance; + class ModernViewportCameraController + : public AzFramework::MultiViewportController { public: - explicit ModernViewportCameraControllerInstance(AzFramework::ViewportId viewportId); - ~ModernViewportCameraControllerInstance(); + using CameraListBuilder = AZStd::function; + //! Sets the camera list builder callback used to populate new ModernViewportCameraControllerInstances + void SetCameraListBuilderCallback(const CameraListBuilder& builder); + + //! Sets up a camera list based on this controller's CameraListBuilderCallback + void SetupCameras(AzFramework::Cameras& cameras); + + private: + CameraListBuilder m_cameraListBuilder; + }; + + class ModernViewportCameraControllerInstance final + : public AzFramework::MultiViewportControllerInstanceInterface + , private AzFramework::ViewportDebugDisplayEventBus::Handler + { + public: + explicit ModernViewportCameraControllerInstance(AzFramework::ViewportId viewportId, ModernViewportCameraController* controller); + ~ModernViewportCameraControllerInstance() override; // MultiViewportControllerInstanceInterface overrides ... bool HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) override; @@ -51,6 +68,4 @@ namespace SandboxEditor AZ::RPI::ViewportContext::MatrixChangedEvent::Handler m_cameraViewMatrixChangeHandler; }; - - using ModernViewportCameraController = AzFramework::MultiViewportController; } // namespace SandboxEditor diff --git a/Code/Sandbox/Editor/ViewportManipulatorController.cpp b/Code/Sandbox/Editor/ViewportManipulatorController.cpp index 43d2fb1f2c..0bf2bbc412 100644 --- a/Code/Sandbox/Editor/ViewportManipulatorController.cpp +++ b/Code/Sandbox/Editor/ViewportManipulatorController.cpp @@ -27,8 +27,8 @@ static const auto InteractionPriority = AzFramework::ViewportControllerPriority: namespace SandboxEditor { -ViewportManipulatorControllerInstance::ViewportManipulatorControllerInstance(AzFramework::ViewportId viewport) - : AzFramework::MultiViewportControllerInstanceInterface(viewport) +ViewportManipulatorControllerInstance::ViewportManipulatorControllerInstance(AzFramework::ViewportId viewport, ViewportManipulatorController* controller) + : AzFramework::MultiViewportControllerInstanceInterface(viewport, controller) { } diff --git a/Code/Sandbox/Editor/ViewportManipulatorController.h b/Code/Sandbox/Editor/ViewportManipulatorController.h index 03a823fa64..d5540229c4 100644 --- a/Code/Sandbox/Editor/ViewportManipulatorController.h +++ b/Code/Sandbox/Editor/ViewportManipulatorController.h @@ -19,11 +19,14 @@ namespace SandboxEditor { + class ViewportManipulatorControllerInstance; + using ViewportManipulatorController = AzFramework::MultiViewportController; + class ViewportManipulatorControllerInstance final - : public AzFramework::MultiViewportControllerInstanceInterface + : public AzFramework::MultiViewportControllerInstanceInterface { public: - explicit ViewportManipulatorControllerInstance(AzFramework::ViewportId viewport); + explicit ViewportManipulatorControllerInstance(AzFramework::ViewportId viewport, ViewportManipulatorController* controller); bool HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) override; void ResetInputChannels() override; @@ -40,6 +43,4 @@ namespace SandboxEditor AZStd::unordered_map m_pendingDoubleClicks; AZ::ScriptTimePoint m_curTime; }; - - using ViewportManipulatorController = AzFramework::MultiViewportController; } //namespace SandboxEditor diff --git a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerListModel.cpp b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerListModel.cpp index cd423535f2..ac9b92adce 100644 --- a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerListModel.cpp +++ b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/UI/Outliner/OutlinerListModel.cpp @@ -1473,14 +1473,15 @@ void OutlinerListModel::OnEntityInfoUpdatedRemoveChildBegin(AZ::EntityId parentI emit EnableSelectionUpdates(false); auto parentIndex = GetIndexFromEntity(parentId); auto childIndex = GetIndexFromEntity(childId); - beginRemoveRows(parentIndex, childIndex.row(), childIndex.row()); + beginResetModel(); } void OutlinerListModel::OnEntityInfoUpdatedRemoveChildEnd(AZ::EntityId parentId, AZ::EntityId childId) { (void)childId; AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - endRemoveRows(); + + endResetModel(); //must refresh partial lock/visibility of parents m_isFilterDirty = true; diff --git a/Code/Tools/AssetProcessor/native/resourcecompiler/RCBuilder.cpp b/Code/Tools/AssetProcessor/native/resourcecompiler/RCBuilder.cpp index 30a7978c50..19b516b681 100644 --- a/Code/Tools/AssetProcessor/native/resourcecompiler/RCBuilder.cpp +++ b/Code/Tools/AssetProcessor/native/resourcecompiler/RCBuilder.cpp @@ -124,131 +124,29 @@ namespace AssetProcessor NativeLegacyRCCompiler::NativeLegacyRCCompiler() : m_resourceCompilerInitialized(false) - , m_systemRoot() - , m_rcExecutableFullPath() , m_requestedQuit(false) { } - - bool NativeLegacyRCCompiler::Initialize(const QString& systemRoot, const QString& rcExecutableFullPath) + + bool NativeLegacyRCCompiler::Initialize() { - // QFile::exists(normalizedPath) - if (!QDir(systemRoot).exists()) - { - AZ_TracePrintf(AssetProcessor::DebugChannel, QString("Cannot locate system root dir %1").arg(systemRoot).toUtf8().data()); - return false; - } - - if (!AZ::IO::SystemFile::Exists(rcExecutableFullPath.toUtf8().data())) - { - AZ_TracePrintf(AssetProcessor::DebugChannel, QString("Invalid executable path '%1'").arg(rcExecutableFullPath).toUtf8().data()); - return false; - } - this->m_systemRoot.setPath(systemRoot); - this->m_rcExecutableFullPath = rcExecutableFullPath; this->m_resourceCompilerInitialized = true; return true; } - bool NativeLegacyRCCompiler::Execute(const QString& inputFile, const QString& watchFolder, const QString& platformIdentifier, - const QString& params, const QString& dest, const AssetBuilderSDK::JobCancelListener* jobCancelListener, Result& result) const + bool NativeLegacyRCCompiler::Execute( + [[maybe_unused]] const QString& inputFile, + [[maybe_unused]] const QString& watchFolder, + [[maybe_unused]] const QString& platformIdentifier, + [[maybe_unused]] const QString& params, + [[maybe_unused]] const QString& dest, + [[maybe_unused]] const AssetBuilderSDK::JobCancelListener* jobCancelListener, + [[maybe_unused]] Result& result) const { - if (!this->m_resourceCompilerInitialized) - { - result.m_exitCode = JobExitCode_RCCouldNotBeLaunched; - result.m_crashed = false; - AZ_Warning("RC Builder", false, "RC Compiler has not been initialized before use."); - return false; - } + // running RC.EXE is deprecated. + AZ_Error("RC Builder", false, "running RC.EXE is deprecated"); - // build the command line: - QString commandString = NativeLegacyRCCompiler::BuildCommand(inputFile, watchFolder, platformIdentifier, params, dest); - - AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; - - // while it might be tempting to set the executable in processLaunchInfo.m_processExecutableString, it turns out that RC.EXE - // won't work if you do that because it assumes the first command line param is the exe name, which is not the case if you do it that way... - - QString formatter("\"%1\" %2"); - processLaunchInfo.m_commandlineParameters = QString(formatter).arg(m_rcExecutableFullPath).arg(commandString).toUtf8().data(); - processLaunchInfo.m_showWindow = false; - processLaunchInfo.m_workingDirectory = m_systemRoot.absolutePath().toUtf8().data(); - processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_IDLE; - - AZ_TracePrintf("RC Builder", "Executing RC.EXE: '%s' ...\n", processLaunchInfo.m_commandlineParameters.c_str()); - AZ_TracePrintf("Rc Builder", "Executing RC.EXE with working directory: '%s' ...\n", processLaunchInfo.m_workingDirectory.c_str()); - - AzFramework::ProcessWatcher* watcher = AzFramework::ProcessWatcher::LaunchProcess(processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT); - - if (!watcher) - { - result.m_exitCode = JobExitCode_RCCouldNotBeLaunched; - result.m_crashed = false; - AZ_Error("RC Builder", false, "RC failed to execute\n"); - - return false; - } - - QElapsedTimer ticker; - ticker.start(); - - // it created the process, wait for it to exit: - bool finishedOK = false; - { - CommunicatorTracePrinter tracer(watcher->GetCommunicator(), "RC Builder"); // allow this to go out of scope... - while ((!m_requestedQuit) && (!finishedOK)) - { - AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(NativeLegacyRCCompiler::s_maxSleepTime)); - - tracer.Pump(); - - if (ticker.elapsed() > s_jobMaximumWaitTime || (jobCancelListener && jobCancelListener->IsCancelled())) - { - break; - } - - AZ::u32 exitCode = 0; - if (!watcher->IsProcessRunning(&exitCode)) - { - finishedOK = true; // we either cant wait for it, or it finished. - result.m_exitCode = exitCode; - result.m_crashed = (exitCode == 100) || (exitCode == 101); // these indicate fatal errors. - break; - } - } - tracer.Pump(); // empty whats left if possible. - } - if (!finishedOK) - { - if (watcher->IsProcessRunning()) - { - watcher->TerminateProcess(0xFFFFFFFF); - } - - if (!this->m_requestedQuit) - { - if (jobCancelListener == nullptr || !jobCancelListener->IsCancelled()) - { - AZ_Error("RC Builder", false, "RC failed to complete within the maximum allowed time and was terminated. please see %s/rc_log.log for details", result.m_outputDir.toUtf8().data()); - } - else - { - AZ_TracePrintf("RC Builder", "RC was terminated. There was a request to cancel the job.\n"); - result.m_exitCode = JobExitCode_JobCancelled; - } - } - else - { - AZ_Warning("RC Builder", false, "RC terminated because the application is shutting down.\n"); - result.m_exitCode = JobExitCode_JobCancelled; - } - result.m_crashed = false; - } - AZ_TracePrintf("RC Builder", "RC.EXE execution has ended\n"); - - delete watcher; - - return finishedOK; + return false; } QString NativeLegacyRCCompiler::BuildCommand(const QString& inputFile, const QString& watchFolder, const QString& platformIdentifier, const QString& params, const QString& dest) @@ -438,22 +336,7 @@ namespace AssetProcessor bool InternalRecognizerBasedBuilder::Initialize(const RecognizerConfiguration& recognizerConfig) { InitializeAssetRecognizers(recognizerConfig.GetAssetRecognizerContainer()); - - // Get the engine root since rc.exe will exist there and not in any external project folder - QString systemRoot; - QString rcFullPath; - - // Validate that the engine root contains the necessary rc.exe - if (!FindRC(rcFullPath)) - { - return false; - } - if (!m_rcCompiler->Initialize(systemRoot, rcFullPath)) - { - AssetBuilderSDK::BuilderLog(m_internalRecognizerBuilderUuid, "Unable to find rc.exe from the engine root (%1).", rcFullPath.toUtf8().data()); - return false; - } - return true; + return m_rcCompiler->Initialize(); } diff --git a/Code/Tools/AssetProcessor/native/resourcecompiler/RCBuilder.h b/Code/Tools/AssetProcessor/native/resourcecompiler/RCBuilder.h index 06facc5eba..56c0091aee 100644 --- a/Code/Tools/AssetProcessor/native/resourcecompiler/RCBuilder.h +++ b/Code/Tools/AssetProcessor/native/resourcecompiler/RCBuilder.h @@ -31,7 +31,7 @@ namespace AssetProcessor }; virtual ~RCCompiler() = default; - virtual bool Initialize(const QString& systemRoot, const QString& rcExecutableFullPath) = 0; + virtual bool Initialize() = 0; virtual bool Execute(const QString& inputFile, const QString& watchFolder, const QString& platformIdentifier, const QString& params, const QString& dest, const AssetBuilderSDK::JobCancelListener* jobCancelListener, Result& result) const = 0; virtual void RequestQuit() = 0; @@ -44,7 +44,7 @@ namespace AssetProcessor public: NativeLegacyRCCompiler(); - bool Initialize(const QString& systemRoot, const QString& rcExecutableFullPath) override; + bool Initialize() override; bool Execute(const QString& inputFile, const QString& watchFolder, const QString& platformIdentifier, const QString& params, const QString& dest, const AssetBuilderSDK::JobCancelListener* jobCancelListener, Result& result) const override; static QString BuildCommand(const QString& inputFile, const QString& watchFolder, const QString& platformIdentifier, const QString& params, const QString& dest); @@ -53,8 +53,6 @@ namespace AssetProcessor static const int s_maxSleepTime; static const unsigned int s_jobMaximumWaitTime; bool m_resourceCompilerInitialized; - QDir m_systemRoot; - QString m_rcExecutableFullPath; volatile bool m_requestedQuit; }; diff --git a/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCBuilderTest.cpp b/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCBuilderTest.cpp index 07d0a5b85d..86f02be330 100644 --- a/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCBuilderTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCBuilderTest.cpp @@ -43,19 +43,6 @@ TEST_F(RCBuilderTest, Shutdown_NormalShutdown_Requested) } -TEST_F(RCBuilderTest, Initialize_StandardInitialization_Fail) -{ - MockRCCompiler* mockRC = new MockRCCompiler(); - TestInternalRecognizerBasedBuilder test(mockRC); - - MockRecognizerConfiguration configuration; - - mockRC->SetResultInitialize(false); - bool initialization_result = test.Initialize(configuration); - ASSERT_FALSE(initialization_result); -} - - TEST_F(RCBuilderTest, Initialize_StandardInitializationWithDuplicateAndInvalidRecognizers_Valid) { MockRCCompiler* mockRC = new MockRCCompiler(); diff --git a/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCBuilderTest.h b/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCBuilderTest.h index 1e401aa3f0..379db316f9 100644 --- a/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCBuilderTest.h +++ b/Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCBuilderTest.h @@ -34,7 +34,7 @@ public: { } - bool Initialize([[maybe_unused]] const QString& systemRoot, [[maybe_unused]] const QString& rcExecutableFullPath) override + bool Initialize() override { m_initialize++; return m_initializeResult; diff --git a/Code/Tools/AssetProcessor/native/unittests/MockApplicationManager.cpp b/Code/Tools/AssetProcessor/native/unittests/MockApplicationManager.cpp index e918dbc678..9a93fe9a63 100644 --- a/Code/Tools/AssetProcessor/native/unittests/MockApplicationManager.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/MockApplicationManager.cpp @@ -47,7 +47,7 @@ namespace AssetProcessor { } - bool Initialize([[maybe_unused]] const QString& systemRoot, [[maybe_unused]] const QString& rcExecutableFullPath) override + bool Initialize() override { m_initialize++; return m_initializeResult; diff --git a/Code/Tools/ProjectManager/Resources/Android.svg b/Code/Tools/ProjectManager/Resources/Android.svg new file mode 100644 index 0000000000..a4b610a3d6 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Android.svg @@ -0,0 +1,10 @@ + + + Icons / Platform / Android + + + + + + + \ No newline at end of file diff --git a/Code/Tools/ProjectManager/Resources/Linux.svg b/Code/Tools/ProjectManager/Resources/Linux.svg new file mode 100644 index 0000000000..843d60cd82 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Linux.svg @@ -0,0 +1,14 @@ + + + Icons / Platform / Linux + + + + + + + + + + + \ No newline at end of file diff --git a/Code/Tools/ProjectManager/Resources/Windows.svg b/Code/Tools/ProjectManager/Resources/Windows.svg new file mode 100644 index 0000000000..46da6693a1 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Windows.svg @@ -0,0 +1,7 @@ + + + Icons / Platform / Windows + + + + \ No newline at end of file diff --git a/Code/Tools/ProjectManager/Resources/iOS.svg b/Code/Tools/ProjectManager/Resources/iOS.svg new file mode 100644 index 0000000000..871d36f657 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/iOS.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/macOS.svg b/Code/Tools/ProjectManager/Resources/macOS.svg new file mode 100644 index 0000000000..4d433be6a3 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/macOS.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Source/GemCatalog.cpp b/Code/Tools/ProjectManager/Source/GemCatalog.cpp deleted file mode 100644 index 9d89740816..0000000000 --- a/Code/Tools/ProjectManager/Source/GemCatalog.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include - -#include - -namespace O3DE::ProjectManager -{ - GemCatalog::GemCatalog(ProjectManagerWindow* window) - : ScreenWidget(window) - , m_ui(new Ui::GemCatalogClass()) - { - m_ui->setupUi(this); - - ConnectSlotsAndSignals(); - } - - GemCatalog::~GemCatalog() - { - } - - void GemCatalog::ConnectSlotsAndSignals() - { - QObject::connect(m_ui->backButton, &QPushButton::pressed, this, &GemCatalog::HandleBackButton); - QObject::connect(m_ui->confirmButton, &QPushButton::pressed, this, &GemCatalog::HandleConfirmButton); - } - - void GemCatalog::HandleBackButton() - { - m_projectManagerWindow->ChangeToScreen(ProjectManagerScreen::NewProjectSettings); - } - void GemCatalog::HandleConfirmButton() - { - m_projectManagerWindow->ChangeToScreen(ProjectManagerScreen::ProjectsHome); - } - -} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog.ui b/Code/Tools/ProjectManager/Source/GemCatalog.ui deleted file mode 100644 index acc2ea80a1..0000000000 --- a/Code/Tools/ProjectManager/Source/GemCatalog.ui +++ /dev/null @@ -1,231 +0,0 @@ - - - GemCatalogClass - - - - 0 - 0 - 806 - 566 - - - - Form - - - - - - - - Gem Catalog - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cart - - - - - - - Hamburger Menu - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - 0 - 0 - - - - TextLabel - - - - - - - RadioButton - - - - - - - RadioButton - - - - - - - RadioButton - - - - - - - Qt::Horizontal - - - - - - - TextLabel - - - - - - - CheckBox - - - - - - - CheckBox - - - - - - - CheckBox - - - - - - - - - - 0 - 0 - - - - - - - - - - TextLabel - - - - - - - - 0 - 0 - - - - - Atom - - - - - Audio - - - - - Camera - - - - - PhysX - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Back - - - - - - - Create Project - - - - - - - - - - diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalog.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalog.cpp new file mode 100644 index 0000000000..6bb1e4959f --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalog.cpp @@ -0,0 +1,101 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + GemCatalog::GemCatalog(ProjectManagerWindow* window) + : ScreenWidget(window) + { + m_gemModel = new GemModel(this); + + QVBoxLayout* vLayout = new QVBoxLayout(); + setLayout(vLayout); + + QHBoxLayout* hLayout = new QHBoxLayout(); + vLayout->addLayout(hLayout); + + QWidget* filterPlaceholderWidget = new QWidget(); + filterPlaceholderWidget->setFixedWidth(250); + hLayout->addWidget(filterPlaceholderWidget); + + m_gemListView = new GemListView(m_gemModel, this); + hLayout->addWidget(m_gemListView); + + QWidget* inspectorPlaceholderWidget = new QWidget(); + inspectorPlaceholderWidget->setFixedWidth(250); + hLayout->addWidget(inspectorPlaceholderWidget); + + // Temporary back and next buttons until they are centralized and shared. + QDialogButtonBox* backNextButtons = new QDialogButtonBox(); + vLayout->addWidget(backNextButtons); + + QPushButton* tempBackButton = backNextButtons->addButton("Back", QDialogButtonBox::RejectRole); + QPushButton* tempNextButton = backNextButtons->addButton("Next", QDialogButtonBox::AcceptRole); + connect(tempBackButton, &QPushButton::pressed, this, &GemCatalog::HandleBackButton); + connect(tempNextButton, &QPushButton::pressed, this, &GemCatalog::HandleConfirmButton); + + // Start: Temporary gem test data + { + m_gemModel->AddGem(GemInfo("EMotion FX", + "O3DE Foundation", + "EMFX is a real-time character animation system. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + (GemInfo::Android | GemInfo::iOS | GemInfo::macOS | GemInfo::Windows | GemInfo::Linux), + true)); + + m_gemModel->AddGem(O3DE::ProjectManager::GemInfo("Atom", + "O3DE Foundation", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + GemInfo::Android | GemInfo::Windows | GemInfo::Linux | GemInfo::macOS, + true)); + + m_gemModel->AddGem(O3DE::ProjectManager::GemInfo("PhysX", + "O3DE London", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + GemInfo::Android | GemInfo::Linux | GemInfo::macOS, + false)); + + m_gemModel->AddGem(O3DE::ProjectManager::GemInfo("Certificate Manager", + "O3DE Irvine", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + GemInfo::Windows, + false)); + + m_gemModel->AddGem(O3DE::ProjectManager::GemInfo("Cloud Gem Framework", + "O3DE Seattle", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + GemInfo::iOS | GemInfo::Linux, + false)); + + m_gemModel->AddGem(O3DE::ProjectManager::GemInfo("Achievements", + "O3DE Foundation", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + GemInfo::Android | GemInfo::Windows | GemInfo::Linux, + false)); + } + // End: Temporary gem test data + } + + void GemCatalog::HandleBackButton() + { + m_projectManagerWindow->ChangeToScreen(ProjectManagerScreen::NewProjectSettings); + } + void GemCatalog::HandleConfirmButton() + { + m_projectManagerWindow->ChangeToScreen(ProjectManagerScreen::ProjectsHome); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalog.h similarity index 83% rename from Code/Tools/ProjectManager/Source/GemCatalog.h rename to Code/Tools/ProjectManager/Source/GemCatalog/GemCatalog.h index e45d865e58..489752bbe7 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalog.h @@ -13,32 +13,25 @@ #if !defined(Q_MOC_RUN) #include +#include +#include #endif -namespace Ui -{ - class GemCatalogClass; -} - namespace O3DE::ProjectManager { class GemCatalog : public ScreenWidget { - public: explicit GemCatalog(ProjectManagerWindow* window); - ~GemCatalog(); - - protected: - void ConnectSlotsAndSignals() override; + ~GemCatalog() = default; protected slots: void HandleBackButton(); void HandleConfirmButton(); private: - QScopedPointer m_ui; + GemListView* m_gemListView = nullptr; + GemModel* m_gemModel = nullptr; }; - } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp new file mode 100644 index 0000000000..b11c7a7a2c --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp @@ -0,0 +1,25 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include "GemInfo.h" + +namespace O3DE::ProjectManager +{ + GemInfo::GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded) + : m_name(name) + , m_creator(creator) + , m_summary(summary) + , m_platforms(platforms) + , m_isAdded(isAdded) + { + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h new file mode 100644 index 0000000000..8c5040eb84 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h @@ -0,0 +1,56 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#include +#include +#endif + +namespace O3DE::ProjectManager +{ + class GemInfo + { + public: + enum Platform + { + Android = 1 << 0, + iOS = 1 << 1, + Linux = 1 << 2, + macOS = 1 << 3, + Windows = 1 << 4, + NumPlatforms = 5 + }; + Q_DECLARE_FLAGS(Platforms, Platform) + + GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded); + + QString m_name; + QString m_displayName; + AZ::Uuid m_uuid; + QString m_creator; + bool m_isAdded = false; //! Is the gem currently added and enabled in the project? + QString m_summary; + Platforms m_platforms; + QStringList m_features; + QString m_version; + QString m_lastUpdatedDate; + QString m_documentationUrl; + QVector m_dependingGemUuids; + QVector m_conflictingGemUuids; + }; +} // namespace O3DE::ProjectManager + +Q_DECLARE_OPERATORS_FOR_FLAGS(O3DE::ProjectManager::GemInfo::Platforms) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp new file mode 100644 index 0000000000..434a4aeef2 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -0,0 +1,232 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include "GemItemDelegate.h" +#include "GemModel.h" +#include +#include +#include + +namespace O3DE::ProjectManager +{ + GemItemDelegate::GemItemDelegate(GemModel* gemModel, QObject* parent) + : QStyledItemDelegate(parent) + , m_gemModel(gemModel) + { + AddPlatformIcon(GemInfo::Android, ":/Resources/Android.svg"); + AddPlatformIcon(GemInfo::iOS, ":/Resources/iOS.svg"); + AddPlatformIcon(GemInfo::Linux, ":/Resources/Linux.svg"); + AddPlatformIcon(GemInfo::macOS, ":/Resources/macOS.svg"); + AddPlatformIcon(GemInfo::Windows, ":/Resources/Windows.svg"); + } + + void GemItemDelegate::AddPlatformIcon(GemInfo::Platform platform, const QString& iconPath) + { + QPixmap pixmap(iconPath); + qreal aspectRatio = static_cast(pixmap.width()) / pixmap.height(); + m_platformIcons.insert(platform, QIcon(iconPath).pixmap(s_platformIconSize * aspectRatio, s_platformIconSize)); + } + + void GemItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const + { + if (!modelIndex.isValid()) + { + return; + } + + QStyleOptionViewItem options(option); + initStyleOption(&options, modelIndex); + + painter->setRenderHint(QPainter::Antialiasing); + + QRect fullRect, itemRect, contentRect; + CalcRects(options, modelIndex, fullRect, itemRect, contentRect); + + QFont standardFont(options.font); + standardFont.setPixelSize(s_fontSize); + + painter->save(); + painter->setClipping(true); + painter->setClipRect(fullRect); + painter->setFont(options.font); + + // Draw background + painter->fillRect(fullRect, m_backgroundColor); + + // Draw item background + const QColor itemBackgroundColor = options.state & QStyle::State_MouseOver ? m_itemBackgroundColor.lighter(120) : m_itemBackgroundColor; + painter->fillRect(itemRect, itemBackgroundColor); + + // Draw border + if (options.state & QStyle::State_Selected) + { + painter->save(); + QPen borderPen(m_borderColor); + borderPen.setWidth(s_borderWidth); + painter->setPen(borderPen); + painter->drawRect(itemRect); + painter->restore(); + } + + // Gem name + const QString gemName = m_gemModel->GetName(modelIndex); + QFont gemNameFont(options.font); + gemNameFont.setPixelSize(s_gemNameFontSize); + gemNameFont.setBold(true); + QRect gemNameRect = GetTextRect(gemNameFont, gemName, s_gemNameFontSize); + gemNameRect.moveTo(contentRect.left(), contentRect.top()); + + painter->setFont(gemNameFont); + painter->setPen(m_textColor); + painter->drawText(gemNameRect, Qt::TextSingleLine, gemName); + + // Gem creator + const QString gemCreator = m_gemModel->GetCreator(modelIndex); + QRect gemCreatorRect = GetTextRect(standardFont, gemCreator, s_fontSize); + gemCreatorRect.moveTo(contentRect.left(), contentRect.top() + gemNameRect.height()); + + painter->setFont(standardFont); + painter->setPen(m_linkColor); + painter->drawText(gemCreatorRect, Qt::TextSingleLine, gemCreator); + + // Gem summary + const QSize summarySize = QSize(contentRect.width() - s_summaryStartX - s_buttonWidth - s_itemMargins.right() * 4, contentRect.height()); + const QRect summaryRect = QRect(/*topLeft=*/QPoint(contentRect.left() + s_summaryStartX, contentRect.top()), summarySize); + + painter->setFont(standardFont); + painter->setPen(m_textColor); + + const QString summary = m_gemModel->GetSummary(modelIndex); + painter->drawText(summaryRect, Qt::AlignLeft | Qt::TextWordWrap, summary); + + + DrawButton(painter, contentRect, modelIndex); + DrawPlatformIcons(painter, contentRect, modelIndex); + + painter->restore(); + } + + QSize GemItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const + { + QStyleOptionViewItem options(option); + initStyleOption(&options, modelIndex); + + int marginsHorizontal = s_itemMargins.left() + s_itemMargins.right() + s_contentMargins.left() + s_contentMargins.right(); + return QSize(marginsHorizontal + s_buttonWidth + s_summaryStartX, s_height); + } + + bool GemItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) + { + if (!modelIndex.isValid()) + { + return false; + } + + return QStyledItemDelegate::editorEvent(event, model, option, modelIndex); + } + + void GemItemDelegate::CalcRects(const QStyleOptionViewItem& option, const QModelIndex& modelIndex, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const + { + const bool isFirst = modelIndex.row() == 0; + + outFullRect = QRect(option.rect); + outItemRect = QRect(outFullRect.adjusted(s_itemMargins.left(), isFirst ? s_itemMargins.top() * 2 : s_itemMargins.top(), -s_itemMargins.right(), -s_itemMargins.bottom())); + outContentRect = QRect(outItemRect.adjusted(s_contentMargins.left(), s_contentMargins.top(), -s_contentMargins.right(), -s_contentMargins.bottom())); + } + + QRect GemItemDelegate::GetTextRect(QFont& font, const QString& text, qreal fontSize) const + { + font.setPixelSize(fontSize); + return QFontMetrics(font).boundingRect(text); + } + + QRect GemItemDelegate::CalcButtonRect(const QRect& contentRect) const + { + const QPoint topLeft = QPoint(contentRect.right() - s_buttonWidth - s_itemMargins.right(), contentRect.top() + contentRect.height() / 2 - s_buttonHeight / 2); + const QSize size = QSize(s_buttonWidth, s_buttonHeight); + return QRect(topLeft, size); + } + + void GemItemDelegate::DrawPlatformIcons(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const + { + const GemInfo::Platforms platforms = m_gemModel->GetPlatforms(modelIndex); + int startX = 0; + + // Iterate and draw the platforms in the order they are defined in the enum. + for (int i = 0; i < GemInfo::NumPlatforms; ++i) + { + // Check if the platform is supported by the given gem. + const GemInfo::Platform platform = static_cast(1 << i); + if (platforms & platform) + { + // Get the icon for the platform and draw it. + const auto iterator = m_platformIcons.find(platform); + if (iterator != m_platformIcons.end()) + { + const QPixmap& pixmap = iterator.value(); + painter->drawPixmap(contentRect.left() + startX, contentRect.bottom() - s_platformIconSize, pixmap); + qreal aspectRatio = static_cast(pixmap.width()) / pixmap.height(); + startX += s_platformIconSize * aspectRatio + s_platformIconSize / 2.5; + } + } + } + } + + void GemItemDelegate::DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const + { + painter->save(); + const QRect buttonRect = CalcButtonRect(contentRect); + QPoint circleCenter; + QString buttonText; + + const bool isAdded = m_gemModel->IsAdded(modelIndex); + if (isAdded) + { + painter->setBrush(m_buttonEnabledColor); + painter->setPen(m_buttonEnabledColor); + + circleCenter = buttonRect.center() + QPoint(buttonRect.width() / 2 - s_buttonBorderRadius, 1); + buttonText = "Added"; + } + else + { + circleCenter = buttonRect.center() + QPoint(-buttonRect.width() / 2 + s_buttonBorderRadius + 1, 1); + buttonText = "Get"; + } + + // Rounded rect + painter->drawRoundedRect(buttonRect, s_buttonBorderRadius, s_buttonBorderRadius); + + // Text + QFont font; + QRect textRect = GetTextRect(font, buttonText, s_buttonFontSize); + if (isAdded) + { + textRect = QRect(buttonRect.left(), buttonRect.top(), buttonRect.width() - s_buttonCircleRadius * 2.0, buttonRect.height()); + } + else + { + textRect = QRect(buttonRect.left() + s_buttonCircleRadius * 2.0, buttonRect.top(), buttonRect.width() - s_buttonCircleRadius * 2.0, buttonRect.height()); + } + + font.setPixelSize(s_buttonFontSize); + painter->setFont(font); + painter->setPen(m_textColor); + painter->drawText(textRect, Qt::AlignCenter, buttonText); + + // Circle + painter->setBrush(m_textColor); + painter->drawEllipse(circleCenter, s_buttonCircleRadius, s_buttonCircleRadius); + + painter->restore(); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h new file mode 100644 index 0000000000..ee0392e188 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h @@ -0,0 +1,79 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include "GemInfo.h" +#include "GemModel.h" +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QEvent) + +namespace O3DE::ProjectManager +{ + class GemItemDelegate + : public QStyledItemDelegate + { + Q_OBJECT // AUTOMOC + + public: + explicit GemItemDelegate(GemModel* gemModel, QObject* parent = nullptr); + ~GemItemDelegate() = default; + + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override; + bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override; + + private: + void CalcRects(const QStyleOptionViewItem& option, const QModelIndex& modelIndex, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const; + QRect GetTextRect(QFont& font, const QString& text, qreal fontSize) const; + QRect CalcButtonRect(const QRect& contentRect) const; + void DrawPlatformIcons(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const; + void DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const; + + GemModel* m_gemModel = nullptr; + + // Colors + const QColor m_textColor = QColor("#FFFFFF"); + const QColor m_linkColor = QColor("#94D2FF"); + const QColor m_backgroundColor = QColor("#333333"); // Outside of the actual gem item + const QColor m_itemBackgroundColor = QColor("#404040"); // Background color of the gem item + const QColor m_borderColor = QColor("#1E70EB"); + const QColor m_buttonEnabledColor = QColor("#00B931"); + + // Item + inline constexpr static int s_height = 135; // Gem item total height + inline constexpr static qreal s_gemNameFontSize = 16.0; + inline constexpr static qreal s_fontSize = 15.0; + inline constexpr static int s_summaryStartX = 200; + + // Margin and borders + inline constexpr static QMargins s_itemMargins = QMargins(/*left=*/20, /*top=*/10, /*right=*/20, /*bottom=*/10); // Item border distances + inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/15, /*top=*/12, /*right=*/12, /*bottom=*/12); // Distances of the elements within an item to the item borders + inline constexpr static int s_borderWidth = 4; + + // Button + inline constexpr static int s_buttonWidth = 70; + inline constexpr static int s_buttonHeight = 24; + inline constexpr static int s_buttonBorderRadius = 12; + inline constexpr static int s_buttonCircleRadius = s_buttonBorderRadius - 3; + inline constexpr static qreal s_buttonFontSize = 12.0; + + // Platform icons + void AddPlatformIcon(GemInfo::Platform platform, const QString& iconPath); + inline constexpr static int s_platformIconSize = 16; + QHash m_platformIcons; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp new file mode 100644 index 0000000000..ad75272c8f --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp @@ -0,0 +1,34 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include "GemListView.h" +#include "GemItemDelegate.h" +#include +#include +#include + +namespace O3DE::ProjectManager +{ + GemListView::GemListView(GemModel* model, QWidget *parent) : + QListView(parent) + { + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + + QPalette palette; + palette.setColor(QPalette::Window, QColor("#333333")); + setPalette(palette); + + setModel(model); + setSelectionModel(model->GetSelectionModel()); + setItemDelegate(new GemItemDelegate(model, this)); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.h new file mode 100644 index 0000000000..79e16bd211 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.h @@ -0,0 +1,31 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include "GemInfo.h" +#include "GemModel.h" +#include +#endif + +namespace O3DE::ProjectManager +{ + class GemListView + : public QListView + { + Q_OBJECT // AUTOMOC + public: + explicit GemListView(GemModel* model, QWidget *parent = nullptr); + ~GemListView() = default; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp new file mode 100644 index 0000000000..27905fd4d2 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -0,0 +1,72 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include "GemModel.h" + +namespace O3DE::ProjectManager +{ + GemModel::GemModel(QObject* parent) + : QStandardItemModel(parent) + { + m_selectionModel = new QItemSelectionModel(this, parent); + } + + QItemSelectionModel* GemModel::GetSelectionModel() const + { + return m_selectionModel; + } + + void GemModel::AddGem(const GemInfo& gemInfo) + { + QStandardItem* item = new QStandardItem(); + + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + + item->setData(gemInfo.m_name, RoleName); + item->setData(gemInfo.m_creator, RoleCreator); + item->setData(static_cast(gemInfo.m_platforms), RolePlatforms); + item->setData(gemInfo.m_summary, RoleSummary); + item->setData(gemInfo.m_isAdded, RoleIsAdded); + + appendRow(item); + } + + void GemModel::Clear() + { + clear(); + } + + QString GemModel::GetName(const QModelIndex& modelIndex) const + { + return modelIndex.data(RoleName).toString(); + } + + QString GemModel::GetCreator(const QModelIndex& modelIndex) const + { + return modelIndex.data(RoleCreator).toString(); + } + + GemInfo::Platforms GemModel::GetPlatforms(const QModelIndex& modelIndex) const + { + return static_cast(modelIndex.data(RolePlatforms).toInt()); + } + + QString GemModel::GetSummary(const QModelIndex& modelIndex) const + { + return modelIndex.data(RoleSummary).toString(); + } + + bool GemModel::IsAdded(const QModelIndex& modelIndex) const + { + return modelIndex.data(RoleIsAdded).toBool(); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h new file mode 100644 index 0000000000..4ce9de32fc --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -0,0 +1,53 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include "GemInfo.h" +#include +#include +#endif + +namespace O3DE::ProjectManager +{ + class GemModel + : public QStandardItemModel + { + Q_OBJECT // AUTOMOC + + public: + explicit GemModel(QObject* parent = nullptr); + QItemSelectionModel* GetSelectionModel() const; + + void AddGem(const GemInfo& gemInfo); + void Clear(); + + QString GetName(const QModelIndex& modelIndex) const; + QString GetCreator(const QModelIndex& modelIndex) const; + GemInfo::Platforms GetPlatforms(const QModelIndex& modelIndex) const; + QString GetSummary(const QModelIndex& modelIndex) const; + bool IsAdded(const QModelIndex& modelIndex) const; + + private: + enum UserRole + { + RoleName = Qt::UserRole, + RoleCreator, + RolePlatforms, + RoleSummary, + RoleIsAdded + }; + + QItemSelectionModel* m_selectionModel = nullptr; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ScreenFactory.cpp b/Code/Tools/ProjectManager/Source/ScreenFactory.cpp index b07816e69e..9250b71ccd 100644 --- a/Code/Tools/ProjectManager/Source/ScreenFactory.cpp +++ b/Code/Tools/ProjectManager/Source/ScreenFactory.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include #include diff --git a/Code/Tools/ProjectManager/Source/ScreenWidget.h b/Code/Tools/ProjectManager/Source/ScreenWidget.h index b4c4fd190c..ddf4add65b 100644 --- a/Code/Tools/ProjectManager/Source/ScreenWidget.h +++ b/Code/Tools/ProjectManager/Source/ScreenWidget.h @@ -30,7 +30,7 @@ namespace O3DE::ProjectManager } protected: - virtual void ConnectSlotsAndSignals() = 0; + virtual void ConnectSlotsAndSignals() {} ProjectManagerWindow* m_projectManagerWindow; }; diff --git a/Code/Tools/ProjectManager/project_manager.qrc b/Code/Tools/ProjectManager/project_manager.qrc index 408398584b..6509a9f940 100644 --- a/Code/Tools/ProjectManager/project_manager.qrc +++ b/Code/Tools/ProjectManager/project_manager.qrc @@ -1,8 +1,13 @@ - - + + Resources/ProjectManager.qss Resources/Add.svg Resources/Select_Folder.svg Resources/o3de_editor.ico + Resources/Windows.svg + Resources/Android.svg + Resources/iOS.svg + Resources/Linux.svg + Resources/macOS.svg - \ No newline at end of file + diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index 333180ea78..b206cf1456 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -25,9 +25,6 @@ set(FILES Source/NewProjectSettings.h Source/NewProjectSettings.cpp Source/NewProjectSettings.ui - Source/GemCatalog.h - Source/GemCatalog.cpp - Source/GemCatalog.ui Source/ProjectsHome.h Source/ProjectsHome.cpp Source/ProjectsHome.ui @@ -37,4 +34,14 @@ set(FILES Source/EngineSettings.h Source/EngineSettings.cpp Source/EngineSettings.ui + Source/GemCatalog/GemCatalog.h + Source/GemCatalog/GemCatalog.cpp + Source/GemCatalog/GemInfo.h + Source/GemCatalog/GemInfo.cpp + Source/GemCatalog/GemItemDelegate.h + Source/GemCatalog/GemItemDelegate.cpp + Source/GemCatalog/GemListView.h + Source/GemCatalog/GemListView.cpp + Source/GemCatalog/GemModel.h + Source/GemCatalog/GemModel.cpp ) diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpAnimationImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpAnimationImporter.cpp index dd92e6bb8b..0ee25195bc 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpAnimationImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpAnimationImporter.cpp @@ -151,7 +151,7 @@ namespace AZ SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class()->Version(2); // [LYN-2281] Skinned mesh loading fixes + serializeContext->Class()->Version(3); // [LYN-3349] Rolling back rotation change } } diff --git a/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h b/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h index f3d474f053..bd30af3ce7 100644 --- a/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h +++ b/Gems/AWSCore/Code/Include/Private/Configuration/AWSCoreConfiguration.h @@ -25,13 +25,24 @@ namespace AWSCore : AWSCoreInternalRequestBus::Handler { public: - static constexpr const char AWSCORE_CONFIGURATION_FILENAME[] = "awscoreconfiguration.setreg"; + static constexpr const char AWSCoreConfigurationName[] = "AWSCoreConfiguration"; + static constexpr const char AWSCoreConfigurationFileName[] = "awscoreconfiguration.setreg"; - static constexpr const char AWSCORE_RESOURCE_MAPPING_CONFIG_FOLDERNAME[] = "Config"; - static constexpr const char AWSCORE_RESOURCE_MAPPING_CONFIG_FILENAME_KEY[] = "/AWSCore/ResourceMappingConfigFileName"; + static constexpr const char AWSCoreResourceMappingConfigFolderName[] = "Config"; + static constexpr const char AWSCoreResourceMappingConfigFileNameKey[] = "/AWSCore/ResourceMappingConfigFileName"; + + static constexpr const char AWSCoreDefaultProfileName[] = "default"; + static constexpr const char AWSCoreProfileNameKey[] = "/AWSCore/ProfileName"; + + static constexpr const char ProjectSourceFolderNotFoundErrorMessage[] = + "Failed to get project source folder path."; + static constexpr const char ProfileNameNotFoundErrorMessage[] = + "Failed to get profile name, return default value instead."; + static constexpr const char ResourceMappingFileNameNotFoundErrorMessage[] = + "Failed to get resource mapping config file name, return empty value instead."; + static constexpr const char SettingsRegistryLoadFailureErrorMessage[] = + "Failed to load AWSCore settings registry file."; - static constexpr const char AWSCORE_DEFAULT_PROFILE_NAME[] = "default"; - static constexpr const char AWSCORE_PROFILENAME_KEY[] = "/AWSCore/ProfileName"; AWSCoreConfiguration(); ~AWSCoreConfiguration() = default; @@ -55,6 +66,9 @@ namespace AWSCore // Parse values from project .setreg file void ParseSettingsRegistryValues(); + // Reset settings registry data + void ResetSettingsRegistryData(); + AZStd::string m_sourceProjectFolder; AZ::SettingsRegistryImpl m_settingsRegistry; AZStd::string m_profileName; diff --git a/Gems/AWSCore/Code/Include/Private/ResourceMapping/AWSResourceMappingConstants.h b/Gems/AWSCore/Code/Include/Private/ResourceMapping/AWSResourceMappingConstants.h index 2ca203bb96..55360f1c76 100644 --- a/Gems/AWSCore/Code/Include/Private/ResourceMapping/AWSResourceMappingConstants.h +++ b/Gems/AWSCore/Code/Include/Private/ResourceMapping/AWSResourceMappingConstants.h @@ -14,20 +14,20 @@ namespace AWSCore { - static constexpr const char AWS_CHINA_REGION_PREFIX[] = "cn-"; + static constexpr const char AWSChinaRegionPrefix[] = "cn-"; - static constexpr const char AWS_FEATURE_GEM_RESTAPI_ID_KEYNAME_SUFFIX[] = ".RESTApiId"; - static constexpr const char AWS_FEATURE_GEM_RESTAPI_STAGE_KEYNAME_SUFFIX[] = ".RESTApiStage"; + static constexpr const char AWSFeatureGemRESTApiIdKeyNameSuffix[] = ".RESTApiId"; + static constexpr const char AWSFeatureGemRESTApiStageKeyNameSuffix[] = ".RESTApiStage"; - static constexpr const char RESOURCE_MAPPING_ACCOUNTID_KEYNAME[] = "AccountId"; - static constexpr const char RESOURCE_MAPPING_RESOURCES_KEYNAME[] = "AWSResourceMappings"; - static constexpr const char RESOURCE_MAPPING_NAMEID_KEYNAME[] = "Name/ID"; - static constexpr const char RESOURCE_MAPPING_REGION_KEYNAME[] = "Region"; - static constexpr const char RESOURCE_MAPPING_TYPE_KEYNAME[] = "Type"; - static constexpr const char RESOURCE_MAPPING_VERSION_KEYNAME[] = "Version"; + static constexpr const char ResourceMappingAccountIdKeyName[] = "AccountId"; + static constexpr const char ResourceMappingResourcesKeyName[] = "AWSResourceMappings"; + static constexpr const char ResourceMappingNameIdKeyName[] = "Name/ID"; + static constexpr const char ResourceMappingRegionKeyName[] = "Region"; + static constexpr const char ResourceMappingTypeKeyName[] = "Type"; + static constexpr const char ResourceMappingVersionKeyName[] = "Version"; // TODO: move this into an independent file under AWSCore gem, if resource mapping tool can reuse it - static constexpr const char RESOURCE_MAPPING_JSON_SCHEMA[] = + static constexpr const char ResourceMappingJsonSchema[] = R"({ "$schema": "http://json-schema.org/draft-04/schema", "type": "object", diff --git a/Gems/AWSCore/Code/Include/Private/ResourceMapping/AWSResourceMappingManager.h b/Gems/AWSCore/Code/Include/Private/ResourceMapping/AWSResourceMappingManager.h index 9200a9c4ff..6d22c55d2c 100644 --- a/Gems/AWSCore/Code/Include/Private/ResourceMapping/AWSResourceMappingManager.h +++ b/Gems/AWSCore/Code/Include/Private/ResourceMapping/AWSResourceMappingManager.h @@ -45,6 +45,35 @@ namespace AWSCore }; public: + static constexpr const char AWSResourceMappingManagerName[] = "AWSResourceMappingManager"; + static constexpr const char ManagerUnexpectedStatusErrorMessage[] = + "AWSResourceMappingManager is in unexpected status."; + static constexpr const char ResourceMappingFileInvalidPathErrorMessage[] = + "Failed to get resource mapping config file path."; + static constexpr const char ResourceMappingKeyNotFoundErrorMessage[] = + "Failed to find resource mapping key: %s"; + static constexpr const char ResourceMappingFileNotLoadedErrorMessage[] = + "Resource mapping config file is not loaded, please confirm %s is setup correctly."; + static constexpr const char ResourceMappingFileLoadFailureErrorMessage[] = + "Resource mapping config file failed to load, please confirm file is present and in correct format."; + static constexpr const char ResourceMappingRESTApiIdAndStageInconsistentErrorMessage[] = + "Resource mapping %s and %s have inconsistent region value, return empty service url."; + static constexpr const char ResourceMappingRESTApiInvalidServiceUrlErrorMessage[] = + "Unable to format REST Api url with RESTApiId=%s, RESTApiRegion=%s, RESTApiStage=%s, return empty service url."; + static constexpr const char ResourceMappingFileInvalidJsonFormatErrorMessage[] = + "Failed to read resource mapping config file: %s"; + static constexpr const char ResourceMappingFileInvalidSchemaErrorMessage[] = + "Failed to load resource mapping config file json schema."; + static constexpr const char ResourceMappingFileInvalidContentErrorMessage[] = + "Failed to parse resource mapping config file: %s"; + + enum class Status : AZ::u8 + { + NotLoaded = 0, + Ready = 1, + Error = 2 + }; + AWSResourceMappingManager(); ~AWSResourceMappingManager() = default; @@ -63,7 +92,12 @@ namespace AWSCore const AZStd::string& restApiIdKeyName, const AZStd::string& restApiStageKeyName) const override; void ReloadConfigFile(bool reloadConfigFileName = false) override; + Status GetStatus() const; + private: + // Get resource attribute error message based on the status + AZStd::string GetResourceAttributeErrorMessageByStatus(const AZStd::string& resourceKeyName) const; + // Get resource attribute from resource mappings AZStd::string GetResourceAttribute( AZStd::function getAttributeFunction, @@ -83,6 +117,7 @@ namespace AWSCore // Validate JSON document against schema bool ValidateJsonDocumentAgainstSchema(const rapidjson::Document& jsonDocument); + Status m_status; // Resource mapping related data AZStd::string m_defaultAccountId; AZStd::string m_defaultRegion; diff --git a/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp b/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp index 0445ea830e..3c0f48c058 100644 --- a/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp +++ b/Gems/AWSCore/Code/Source/Configuration/AWSCoreConfiguration.cpp @@ -20,7 +20,7 @@ namespace AWSCore { AWSCoreConfiguration::AWSCoreConfiguration() : m_sourceProjectFolder("") - , m_profileName(AWSCORE_DEFAULT_PROFILE_NAME) + , m_profileName(AWSCoreDefaultProfileName) , m_resourceMappingConfigFileName("") { } @@ -44,16 +44,16 @@ namespace AWSCore { if (m_sourceProjectFolder.empty()) { - AZ_Warning("AWSCoreConfiguration", false, "Failed to get source project folder path."); + AZ_Warning(AWSCoreConfigurationName, false, ProjectSourceFolderNotFoundErrorMessage); return ""; } if (m_resourceMappingConfigFileName.empty()) { - AZ_Warning("AWSCoreConfiguration", false, "Failed to get resource mapping config file name."); + AZ_Warning(AWSCoreConfigurationName, false, ResourceMappingFileNameNotFoundErrorMessage); return ""; } AZStd::string configFilePath = AZStd::string::format("%s/%s/%s", - m_sourceProjectFolder.c_str(), AWSCORE_RESOURCE_MAPPING_CONFIG_FOLDERNAME, m_resourceMappingConfigFileName.c_str()); + m_sourceProjectFolder.c_str(), AWSCoreResourceMappingConfigFolderName, m_resourceMappingConfigFileName.c_str()); AzFramework::StringFunc::Path::Normalize(configFilePath); return configFilePath; } @@ -68,17 +68,17 @@ namespace AWSCore { if (m_sourceProjectFolder.empty()) { - AZ_Warning("AWSCoreConfiguration", false, "Failed to get source project folder path."); + AZ_Warning(AWSCoreConfigurationName, false, ProjectSourceFolderNotFoundErrorMessage); return; } AZStd::string settingsRegistryPath = AZStd::string::format("%s/%s/%s", - m_sourceProjectFolder.c_str(), AZ::SettingsRegistryInterface::RegistryFolder, AWSCoreConfiguration::AWSCORE_CONFIGURATION_FILENAME); + m_sourceProjectFolder.c_str(), AZ::SettingsRegistryInterface::RegistryFolder, AWSCoreConfiguration::AWSCoreConfigurationFileName); AzFramework::StringFunc::Path::Normalize(settingsRegistryPath); if (!m_settingsRegistry.MergeSettingsFile(settingsRegistryPath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, "")) { - AZ_Warning("AWSCoreConfiguration", false, "Failed to merge AWS core settings registry."); + AZ_Warning(AWSCoreConfigurationName, false, SettingsRegistryLoadFailureErrorMessage); return; } @@ -90,7 +90,7 @@ namespace AWSCore auto sourceProjectFolder = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@"); if (!sourceProjectFolder) { - AZ_Error("AWSCoreConfiguration", false, "Failed to initialize source project folder path."); + AZ_Error(AWSCoreConfigurationName, false, ProjectSourceFolderNotFoundErrorMessage); } else { @@ -102,24 +102,38 @@ namespace AWSCore { m_resourceMappingConfigFileName.clear(); auto resourceMappingConfigFileNamePath = AZStd::string::format("%s%s", - AZ::SettingsRegistryMergeUtils::OrganizationRootKey, AWSCORE_RESOURCE_MAPPING_CONFIG_FILENAME_KEY); + AZ::SettingsRegistryMergeUtils::OrganizationRootKey, AWSCoreResourceMappingConfigFileNameKey); if (!m_settingsRegistry.Get(m_resourceMappingConfigFileName, resourceMappingConfigFileNamePath)) { - AZ_Warning("AWSCoreConfiguration", false, "Failed to get resource mapping config file name from settings registry."); + AZ_Warning(AWSCoreConfigurationName, false, ResourceMappingFileNameNotFoundErrorMessage); } m_profileName.clear(); auto profileNamePath = AZStd::string::format( - "%s%s", AZ::SettingsRegistryMergeUtils::OrganizationRootKey, AWSCORE_PROFILENAME_KEY); + "%s%s", AZ::SettingsRegistryMergeUtils::OrganizationRootKey, AWSCoreProfileNameKey); if (!m_settingsRegistry.Get(m_profileName, profileNamePath)) { - AZ_Warning("AWSCoreConfiguration", false, "Failed to get profile name from settings registry, using default value instead."); - m_profileName = AWSCORE_DEFAULT_PROFILE_NAME; + AZ_Warning(AWSCoreConfigurationName, false, ProfileNameNotFoundErrorMessage); + m_profileName = AWSCoreDefaultProfileName; } } + void AWSCoreConfiguration::ResetSettingsRegistryData() + { + auto profileNamePath = AZStd::string::format("%s%s", + AZ::SettingsRegistryMergeUtils::OrganizationRootKey, AWSCoreProfileNameKey); + m_settingsRegistry.Remove(profileNamePath); + m_profileName.clear(); + + auto resourceMappingConfigFileNamePath = AZStd::string::format("%s%s", + AZ::SettingsRegistryMergeUtils::OrganizationRootKey, AWSCoreResourceMappingConfigFileNameKey); + m_settingsRegistry.Remove(resourceMappingConfigFileNamePath); + m_resourceMappingConfigFileName.clear(); + } + void AWSCoreConfiguration::ReloadConfiguration() { + ResetSettingsRegistryData(); InitSettingsRegistry(); } } // namespace AWSCore diff --git a/Gems/AWSCore/Code/Source/Credential/AWSDefaultCredentialHandler.cpp b/Gems/AWSCore/Code/Source/Credential/AWSDefaultCredentialHandler.cpp index ba7a8c72dd..bb195e487a 100644 --- a/Gems/AWSCore/Code/Source/Credential/AWSDefaultCredentialHandler.cpp +++ b/Gems/AWSCore/Code/Source/Credential/AWSDefaultCredentialHandler.cpp @@ -84,7 +84,7 @@ namespace AWSCore { AZ_Warning("AWSDefaultCredentialHandler", false, "Failed to get profile name, use default profile name instead"); SetProfileCredentialsProvider(Aws::MakeShared( - AWSDEFAULTCREDENTIALHANDLER_ALLOC_TAG, AWSCoreConfiguration::AWSCORE_DEFAULT_PROFILE_NAME)); + AWSDEFAULTCREDENTIALHANDLER_ALLOC_TAG, AWSCoreConfiguration::AWSCoreDefaultProfileName)); } else { diff --git a/Gems/AWSCore/Code/Source/ResourceMapping/AWSResourceMappingManager.cpp b/Gems/AWSCore/Code/Source/ResourceMapping/AWSResourceMappingManager.cpp index c87a7f99ba..efdba0e9e0 100644 --- a/Gems/AWSCore/Code/Source/ResourceMapping/AWSResourceMappingManager.cpp +++ b/Gems/AWSCore/Code/Source/ResourceMapping/AWSResourceMappingManager.cpp @@ -12,12 +12,14 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -25,7 +27,8 @@ namespace AWSCore { AWSResourceMappingManager::AWSResourceMappingManager() - : m_defaultAccountId("") + : m_status(Status::NotLoaded) + , m_defaultAccountId("") , m_defaultRegion("") , m_resourceMappings() { @@ -43,11 +46,27 @@ namespace AWSCore ResetResourceMappingsData(); } + AZStd::string AWSResourceMappingManager::GetResourceAttributeErrorMessageByStatus(const AZStd::string& resourceKeyName) const + { + switch (m_status) + { + case Status::NotLoaded: + return AZStd::string::format(ResourceMappingFileNotLoadedErrorMessage, AWSCoreConfiguration::AWSCoreConfigurationFileName); + case Status::Ready: + return AZStd::string::format(ResourceMappingKeyNotFoundErrorMessage, resourceKeyName.c_str()); + case Status::Error: + return ResourceMappingFileLoadFailureErrorMessage; + default: + return ManagerUnexpectedStatusErrorMessage; + } + } + AZStd::string AWSResourceMappingManager::GetDefaultAccountId() const { if (m_defaultAccountId.empty()) { - AZ_Warning("AWSResourceMappingManager", false, "Account Id should not be empty, please make sure config file is valid."); + AZ_Warning(AWSResourceMappingManagerName, false, + GetResourceAttributeErrorMessageByStatus(ResourceMappingAccountIdKeyName).c_str()); } return m_defaultAccountId; } @@ -56,7 +75,8 @@ namespace AWSCore { if (m_defaultRegion.empty()) { - AZ_Warning("AWSResourceMappingManager", false, "Region should not be empty, please make sure config file is valid."); + AZ_Warning(AWSResourceMappingManagerName, false, + GetResourceAttributeErrorMessageByStatus(ResourceMappingRegionKeyName).c_str()); } return m_defaultRegion; } @@ -100,8 +120,8 @@ namespace AWSCore AZStd::string AWSResourceMappingManager::GetServiceUrlByServiceName(const AZStd::string& serviceName) const { return GetServiceUrlByRESTApiIdAndStage( - AZStd::string::format("%s%s", serviceName.c_str(), AWS_FEATURE_GEM_RESTAPI_ID_KEYNAME_SUFFIX), - AZStd::string::format("%s%s", serviceName.c_str(), AWS_FEATURE_GEM_RESTAPI_STAGE_KEYNAME_SUFFIX)); + AZStd::string::format("%s%s", serviceName.c_str(), AWSFeatureGemRESTApiIdKeyNameSuffix), + AZStd::string::format("%s%s", serviceName.c_str(), AWSFeatureGemRESTApiStageKeyNameSuffix)); } AZStd::string AWSResourceMappingManager::GetServiceUrlByRESTApiIdAndStage( @@ -113,16 +133,13 @@ namespace AWSCore AZStd::string serviceRegion = GetResourceRegion(restApiIdKeyName); if (serviceRegion != GetResourceRegion(restApiStageKeyName)) { - AZ_Warning( - "AWSResourceMappingManager", false, "%s and %s have inconsistent region value, return empty service url.", + AZ_Warning(AWSResourceMappingManagerName, false, ResourceMappingRESTApiIdAndStageInconsistentErrorMessage, restApiIdKeyName.c_str(), restApiStageKeyName.c_str()); return ""; } AZStd::string serviceRESTApiUrl = AWSResourceMappingUtils::FormatRESTApiUrl(serviceRESTApiId, serviceRegion, serviceRESTApiStage); - AZ_Warning( - "AWSResourceMappingManager", !serviceRESTApiUrl.empty(), - "Unable to format REST Api url with RESTApiId=%s, RESTApiRegion=%s, RESTApiStage=%s, return empty service url.", + AZ_Warning(AWSResourceMappingManagerName, !serviceRESTApiUrl.empty(), ResourceMappingRESTApiInvalidServiceUrlErrorMessage, serviceRESTApiId.c_str(), serviceRegion.c_str(), serviceRESTApiStage.c_str()); return serviceRESTApiUrl; } @@ -136,16 +153,21 @@ namespace AWSCore return getAttributeFunction(iter->second); } - AZ_Warning("AWSResourceMappingManager", false, "Failed to find resource mapping key: %s.", resourceKeyName.c_str()); + AZ_Warning(AWSResourceMappingManagerName, false, GetResourceAttributeErrorMessageByStatus(resourceKeyName).c_str()); return ""; } + AWSResourceMappingManager::Status AWSResourceMappingManager::GetStatus() const + { + return m_status; + } + void AWSResourceMappingManager::ParseJsonDocument(const rapidjson::Document& jsonDocument) { - m_defaultAccountId = jsonDocument.FindMember(RESOURCE_MAPPING_ACCOUNTID_KEYNAME)->value.GetString(); - m_defaultRegion = jsonDocument.FindMember(RESOURCE_MAPPING_REGION_KEYNAME)->value.GetString(); + m_defaultAccountId = jsonDocument.FindMember(ResourceMappingAccountIdKeyName)->value.GetString(); + m_defaultRegion = jsonDocument.FindMember(ResourceMappingRegionKeyName)->value.GetString(); - auto resourceMappings = jsonDocument.FindMember(RESOURCE_MAPPING_RESOURCES_KEYNAME)->value.GetObject(); + auto resourceMappings = jsonDocument.FindMember(ResourceMappingResourcesKeyName)->value.GetObject(); for (auto mappingIter = resourceMappings.MemberBegin(); mappingIter != resourceMappings.MemberEnd(); mappingIter++) { auto mappingValue = mappingIter->value.GetObject(); @@ -162,16 +184,16 @@ namespace AWSCore const JsonObject& jsonObject) { AWSResourceMappingAttributes attributes; - if (jsonObject.HasMember(RESOURCE_MAPPING_ACCOUNTID_KEYNAME)) + if (jsonObject.HasMember(ResourceMappingAccountIdKeyName)) { - attributes.resourceAccountId = jsonObject.FindMember(RESOURCE_MAPPING_ACCOUNTID_KEYNAME)->value.GetString(); + attributes.resourceAccountId = jsonObject.FindMember(ResourceMappingAccountIdKeyName)->value.GetString(); } - attributes.resourceNameId = jsonObject.FindMember(RESOURCE_MAPPING_NAMEID_KEYNAME)->value.GetString(); - if (jsonObject.HasMember(RESOURCE_MAPPING_REGION_KEYNAME)) + attributes.resourceNameId = jsonObject.FindMember(ResourceMappingNameIdKeyName)->value.GetString(); + if (jsonObject.HasMember(ResourceMappingRegionKeyName)) { - attributes.resourceRegion = jsonObject.FindMember(RESOURCE_MAPPING_REGION_KEYNAME)->value.GetString(); + attributes.resourceRegion = jsonObject.FindMember(ResourceMappingRegionKeyName)->value.GetString(); } - attributes.resourceType = jsonObject.FindMember(RESOURCE_MAPPING_TYPE_KEYNAME)->value.GetString(); + attributes.resourceType = jsonObject.FindMember(ResourceMappingTypeKeyName)->value.GetString(); return attributes; } @@ -188,7 +210,7 @@ namespace AWSCore AWSCoreInternalRequestBus::BroadcastResult(configJsonPath, &AWSCoreInternalRequests::GetResourceMappingConfigFilePath); if (configJsonPath.empty()) { - AZ_Warning("AWSResourceMappingManager", false, "Failed to get resource mapping config file path."); + AZ_Warning(AWSResourceMappingManagerName, false, ResourceMappingFileInvalidPathErrorMessage); return; } @@ -201,20 +223,26 @@ namespace AWSCore if (!ValidateJsonDocumentAgainstSchema(jsonDocument)) { // Failed to satisfy the validation against json schema + m_status = Status::Error; return; } ParseJsonDocument(jsonDocument); } else { - AZ_Warning( - "AWSResourceMappingManager", false, "Failed to get read resource mapping config file: %s\n Error: %s", - configJsonPath.c_str(), readJsonOutcome.GetError().c_str()); + m_status = Status::Error; + AZ_Warning(AWSResourceMappingManagerName, false, + ResourceMappingFileInvalidJsonFormatErrorMessage, readJsonOutcome.GetError().c_str()); + return; } + + // Resource mapping config file gets loaded successfully + m_status = Status::Ready; } void AWSResourceMappingManager::ResetResourceMappingsData() { + m_status = Status::NotLoaded; m_defaultAccountId = ""; m_defaultRegion = ""; m_resourceMappings.clear(); @@ -223,9 +251,9 @@ namespace AWSCore bool AWSResourceMappingManager::ValidateJsonDocumentAgainstSchema(const rapidjson::Document& jsonDocument) { rapidjson::Document jsonSchemaDocument; - if (jsonSchemaDocument.Parse(RESOURCE_MAPPING_JSON_SCHEMA).HasParseError()) + if (jsonSchemaDocument.Parse(ResourceMappingJsonSchema).HasParseError()) { - AZ_Error("AWSResourceMappingManager", false, "Invalid resource mapping json schema."); + AZ_Error(AWSResourceMappingManagerName, false, ResourceMappingFileInvalidSchemaErrorMessage); return false; } @@ -235,12 +263,10 @@ namespace AWSCore if (!jsonDocument.Accept(validator)) { rapidjson::StringBuffer error; - validator.GetInvalidSchemaPointer().StringifyUriFragment(error); - AZ_Warning("AWSResourceMappingManager", false, "Failed to load config file, invalid schema: %s.", error.GetString()); - AZ_Warning("AWSResourceMappingManager", false, "Failed to load config file, invalid keyword: %s.", validator.GetInvalidSchemaKeyword()); - error.Clear(); - validator.GetInvalidDocumentPointer().StringifyUriFragment(error); - AZ_Warning("AWSResourceMappingManager", false, "Failed to load config file, invalid document: %s.", error.GetString()); + rapidjson::PrettyWriter writer(error); + validator.GetError().Accept(writer); + AZ_Warning(AWSResourceMappingManagerName, false, ResourceMappingFileInvalidContentErrorMessage, error.GetString()); + return false; } return true; diff --git a/Gems/AWSCore/Code/Source/ResourceMapping/AWSResourceMappingUtils.cpp b/Gems/AWSCore/Code/Source/ResourceMapping/AWSResourceMappingUtils.cpp index 74be3bf914..7d17a86909 100644 --- a/Gems/AWSCore/Code/Source/ResourceMapping/AWSResourceMappingUtils.cpp +++ b/Gems/AWSCore/Code/Source/ResourceMapping/AWSResourceMappingUtils.cpp @@ -18,8 +18,8 @@ namespace AWSCore namespace AWSResourceMappingUtils { // https://docs.aws.amazon.com/general/latest/gr/apigateway.html - static constexpr char RESTAPI_URL_FORMAT[] = "https://%s.execute-api.%s.amazonaws.com/%s"; - static constexpr char RESTAPI_CHINA_URL_FORMAT[] = "https://%s.execute-api.%s.amazonaws.com.cn/%s"; + static constexpr char RESTApiUrlFormat[] = "https://%s.execute-api.%s.amazonaws.com/%s"; + static constexpr char RESTApiChinaUrlFormat[] = "https://%s.execute-api.%s.amazonaws.com.cn/%s"; AZStd::string FormatRESTApiUrl( const AZStd::string& restApiId, const AZStd::string& restApiRegion, const AZStd::string& restApiStage) @@ -27,14 +27,14 @@ namespace AWSCore // https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-call-api.html if (!restApiId.empty() && !restApiRegion.empty() && !restApiStage.empty()) { - if (restApiRegion.rfind(AWS_CHINA_REGION_PREFIX, 0) == 0) + if (restApiRegion.rfind(AWSChinaRegionPrefix, 0) == 0) { - return AZStd::string::format(RESTAPI_CHINA_URL_FORMAT, + return AZStd::string::format(RESTApiChinaUrlFormat, restApiId.c_str(), restApiRegion.c_str(), restApiStage.c_str()); } else { - return AZStd::string::format(RESTAPI_URL_FORMAT, + return AZStd::string::format(RESTApiUrlFormat, restApiId.c_str(), restApiRegion.c_str(), restApiStage.c_str()); } } diff --git a/Gems/AWSCore/Code/Tests/Configuration/AWSCoreConfigurationTest.cpp b/Gems/AWSCore/Code/Tests/Configuration/AWSCoreConfigurationTest.cpp index dab405dee4..b3c2b8d2ca 100644 --- a/Gems/AWSCore/Code/Tests/Configuration/AWSCoreConfigurationTest.cpp +++ b/Gems/AWSCore/Code/Tests/Configuration/AWSCoreConfigurationTest.cpp @@ -46,7 +46,7 @@ public: void CreateTestSetRegFile(const AZStd::string& setregContent) { m_normalizedSetRegFilePath = AZStd::string::format("%s/%s", - m_normalizedSetRegFolderPath.c_str(), AWSCore::AWSCoreConfiguration::AWSCORE_CONFIGURATION_FILENAME); + m_normalizedSetRegFolderPath.c_str(), AWSCore::AWSCoreConfiguration::AWSCoreConfigurationFileName); AzFramework::StringFunc::Path::Normalize(m_normalizedSetRegFilePath); CreateTestFile(m_normalizedSetRegFilePath, setregContent); } @@ -177,7 +177,7 @@ TEST_F(AWSCoreConfigurationTest, ReloadConfiguration_LoadValidSettingsRegistryAf auto actualConfigFilePath = m_awsCoreConfiguration->GetResourceMappingConfigFilePath(); auto actualProfileName = m_awsCoreConfiguration->GetProfileName(); EXPECT_TRUE(actualConfigFilePath.empty()); - EXPECT_TRUE(actualProfileName == AWSCoreConfiguration::AWSCORE_DEFAULT_PROFILE_NAME); + EXPECT_TRUE(actualProfileName == AWSCoreConfiguration::AWSCoreDefaultProfileName); CreateTestSetRegFile(TEST_VALID_RESOURCE_MAPPING_SETREG); m_awsCoreConfiguration->ReloadConfiguration(); @@ -185,5 +185,24 @@ TEST_F(AWSCoreConfigurationTest, ReloadConfiguration_LoadValidSettingsRegistryAf actualConfigFilePath = m_awsCoreConfiguration->GetResourceMappingConfigFilePath(); actualProfileName = m_awsCoreConfiguration->GetProfileName(); EXPECT_FALSE(actualConfigFilePath.empty()); - EXPECT_TRUE(actualProfileName != AWSCoreConfiguration::AWSCORE_DEFAULT_PROFILE_NAME); + EXPECT_TRUE(actualProfileName != AWSCoreConfiguration::AWSCoreDefaultProfileName); +} + +TEST_F(AWSCoreConfigurationTest, ReloadConfiguration_LoadInvalidSettingsRegistryAfterValidOne_ReturnEmptyConfigFilePath) +{ + CreateTestSetRegFile(TEST_VALID_RESOURCE_MAPPING_SETREG); + m_awsCoreConfiguration->InitConfig(); + + auto actualConfigFilePath = m_awsCoreConfiguration->GetResourceMappingConfigFilePath(); + auto actualProfileName = m_awsCoreConfiguration->GetProfileName(); + EXPECT_FALSE(actualConfigFilePath.empty()); + EXPECT_TRUE(actualProfileName != AWSCoreConfiguration::AWSCoreDefaultProfileName); + + CreateTestSetRegFile(TEST_INVALID_RESOURCE_MAPPING_SETREG); + m_awsCoreConfiguration->ReloadConfiguration(); + + actualConfigFilePath = m_awsCoreConfiguration->GetResourceMappingConfigFilePath(); + actualProfileName = m_awsCoreConfiguration->GetProfileName(); + EXPECT_TRUE(actualConfigFilePath.empty()); + EXPECT_TRUE(actualProfileName == AWSCoreConfiguration::AWSCoreDefaultProfileName); } diff --git a/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp b/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp index 73a28f97cb..3adebc9a24 100644 --- a/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp +++ b/Gems/AWSCore/Code/Tests/ResourceMapping/AWSResourceMappingManagerTest.cpp @@ -98,7 +98,7 @@ public: "AWSResourceMappingManager", AZ::Uuid::CreateRandom().ToString(false, false).c_str()); AzFramework::StringFunc::Path::Normalize(m_normalizedSourceProjectFolder); m_normalizedConfigFolderPath = AZStd::string::format("%s/%s/", - m_normalizedSourceProjectFolder.c_str(), AWSCore::AWSCoreConfiguration::AWSCORE_RESOURCE_MAPPING_CONFIG_FOLDERNAME); + m_normalizedSourceProjectFolder.c_str(), AWSCore::AWSCoreConfiguration::AWSCoreResourceMappingConfigFolderName); AzFramework::StringFunc::Path::Normalize(m_normalizedConfigFolderPath); AWSCoreInternalRequestBus::Handler::BusConnect(); } @@ -178,6 +178,7 @@ TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseInvalidConfigFile_Con EXPECT_EQ(m_reloadConfigurationCounter, 1); EXPECT_TRUE(actualAccountId.empty()); EXPECT_TRUE(actualRegion.empty()); + EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::Error); } TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseValidConfigFile_ConfigDataIsNotEmpty) @@ -192,6 +193,7 @@ TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseValidConfigFile_Confi EXPECT_EQ(m_reloadConfigurationCounter, 1); EXPECT_FALSE(actualAccountId.empty()); EXPECT_FALSE(actualRegion.empty()); + EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::Ready); } TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseValidConfigFile_ConfigDataIsNotEmptyWithMultithreadCalls) @@ -230,11 +232,13 @@ TEST_F(AWSResourceMappingManagerTest, DeactivateManager_AfterActivatingWithValid AWSResourceMappingRequestBus::BroadcastResult(actualRegion, &AWSResourceMappingRequests::GetDefaultRegion); EXPECT_FALSE(actualAccountId.empty()); EXPECT_FALSE(actualRegion.empty()); + EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::Ready); m_resourceMappingManager->DeactivateManager(); EXPECT_TRUE(m_resourceMappingManager->GetDefaultAccountId().empty()); EXPECT_TRUE(m_resourceMappingManager->GetDefaultRegion().empty()); + EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::NotLoaded); } TEST_F(AWSResourceMappingManagerTest, GetDefaultAccountId_AfterParsingValidConfigFile_GetExpectedDefaultAccountId) @@ -416,6 +420,7 @@ TEST_F(AWSResourceMappingManagerTest, ReloadConfigFile_ParseValidConfigFileAfter EXPECT_EQ(m_reloadConfigurationCounter, 1); EXPECT_TRUE(actualAccountId.empty()); EXPECT_TRUE(actualRegion.empty()); + EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::Error); CreateTestConfigFile(TEST_VALID_RESOURCE_MAPPING_CONFIG_FILE); m_resourceMappingManager->ReloadConfigFile(); @@ -425,6 +430,7 @@ TEST_F(AWSResourceMappingManagerTest, ReloadConfigFile_ParseValidConfigFileAfter EXPECT_EQ(m_reloadConfigurationCounter, 1); EXPECT_FALSE(actualAccountId.empty()); EXPECT_FALSE(actualRegion.empty()); + EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::Ready); } TEST_F(AWSResourceMappingManagerTest, ReloadConfigFile_ReloadConfigFileNameAndParseValidConfigFile_ConfigDataGetParsed) @@ -435,6 +441,7 @@ TEST_F(AWSResourceMappingManagerTest, ReloadConfigFile_ReloadConfigFileNameAndPa EXPECT_EQ(m_reloadConfigurationCounter, 1); EXPECT_FALSE(m_resourceMappingManager->GetDefaultAccountId().empty()); EXPECT_FALSE(m_resourceMappingManager->GetDefaultRegion().empty()); + EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::Ready); } TEST_F(AWSResourceMappingManagerTest, ReloadConfigFile_MissingSetRegFile_ConfigDataIsNotParsed) @@ -444,4 +451,5 @@ TEST_F(AWSResourceMappingManagerTest, ReloadConfigFile_MissingSetRegFile_ConfigD EXPECT_EQ(m_reloadConfigurationCounter, 1); EXPECT_TRUE(m_resourceMappingManager->GetDefaultAccountId().empty()); EXPECT_TRUE(m_resourceMappingManager->GetDefaultRegion().empty()); + EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::NotLoaded); } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp index f63ae4ca77..7b6ec518bc 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp @@ -851,7 +851,9 @@ namespace ImageProcessingAtom if (preset == nullptr) { - AZ_Assert(false, "preset should always exist"); + AZStd::string uuidStr; + textureSettings.m_preset.ToString(uuidStr); + AZ_Assert(false, "%s cannot find image preset with ID %s.", imageFilePath.c_str(), uuidStr.c_str()); return nullptr; } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype index cb66a987ae..954e01c592 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype @@ -946,19 +946,6 @@ "type": "Bool", "defaultValue": false }, - { - "id": "factor", - "displayName": "Factor", - "description": "Strength factor for scaling the depth values", - "type": "Float", - "defaultValue": 0.0, - "min": 0.0, - "softMax": 0.1, - "connection": { - "type": "ShaderInput", - "id": "m_depthFactor" - } - }, { "id": "textureMap", "displayName": "Texture Map", @@ -981,6 +968,32 @@ "id": "m_parallaxUvIndex" } }, + { + "id": "factor", + "displayName": "Heightmap Scale", + "description": "The total height of the heightmap in local model units.", + "type": "Float", + "defaultValue": 0.0, + "min": 0.0, + "softMax": 0.1, + "connection": { + "type": "ShaderInput", + "id": "m_depthFactor" + } + }, + { + "id": "offset", + "displayName": "Offset", + "description": "Adjusts the overall displacement amount in local model units.", + "type": "Float", + "defaultValue": 0.0, + "softMin": -0.1, + "softMax": 0.1, + "connection": { + "type": "ShaderInput", + "id": "m_depthOffset" + } + }, { "id": "invert", "displayName": "Invert", @@ -1026,6 +1039,17 @@ "type": "ShaderOption", "id": "o_parallax_enablePixelDepthOffset" } + }, + { + "id": "showClipping", + "displayName": "Show Clipping", + "description": "Highlight areas where the heightmap is clipped by the mesh surface.", + "type": "Bool", + "defaultValue": false, + "connection": { + "type": "ShaderOption", + "id": "o_parallax_highlightClipping" + } } ], "subsurfaceScattering": [ @@ -1714,22 +1738,6 @@ "shaderOption": "o_emissive_useTexture" } }, - { - // See the comment above for details. - "type": "UseTexture", - "args": { - "textureProperty": "parallax.textureMap", - "dependentProperties": ["parallax.textureMapUv"], - "useTextureProperty": "parallax.enable", - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS", - "Shadowmap_WithPS", - "DepthPass_WithPS" - ], - "shaderOption": "o_parallax_feature_enabled" - } - }, { // See the comment above for details. "type": "UseTexture", @@ -1813,34 +1821,6 @@ ] } }, - { - // Controls visibility for properties in the editor. - // @param actions - a list of actions that are executed in order. visibility will be set when triggerProperty hits the triggerValue. - // @param affectedProperties - the properties that are affected by actions. - "type": "UpdatePropertyVisibility", - "args": { - "actions": [ - { - "triggerProperty": "parallax.enable", - "triggerValue": true, - "visibility": "Enabled" - }, - { - "triggerProperty": "parallax.enable", - "triggerValue": false, - "visibility": "Hidden" - } - ], - "affectedProperties": [ - "parallax.factor", - "parallax.textureMap", - "parallax.invert", - "parallax.algorithm", - "parallax.quality", - "parallax.pdo" - ] - } - }, { "type": "UpdatePropertyVisibility", "args": { @@ -2076,6 +2056,12 @@ ] } }, + { + "type": "Lua", + "args": { + "file": "StandardPBR_ParallaxState.lua" + } + }, { "type": "Lua", "args": { diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Common.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Common.azsli index f9bfb75fea..34af9229c2 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Common.azsli @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include "MaterialInputs/BaseColorInput.azsli" #include "MaterialInputs/RoughnessInput.azsli" @@ -104,7 +106,22 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial } // Callback function for ParallaxMapping.azsli -float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) +DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) { return SampleDepthOrHeightMap(MaterialSrg::m_depthInverted, MaterialSrg::m_depthMap, MaterialSrg::m_sampler, uv, uv_ddx, uv_ddy); } + +COMMON_OPTIONS_PARALLAX() + +bool ShouldHandleParallax() +{ + // Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scattering is enabled. + return !o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useDepthMap; +} + +bool ShouldHandleParallaxInDepthShaders() +{ + // The depth pass shaders need to calculate parallax when the result could affect the depth buffer, or when + // parallax could affect texel clipping. + return ShouldHandleParallax() && (o_parallax_enablePixelDepthOffset || o_opacity_mode == OpacityMode::Cutout); +} diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl index 28968b5941..db99ee7e24 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl @@ -10,7 +10,6 @@ * */ -#include #include "./EnhancedPBR_Common.azsli" #include #include @@ -55,7 +54,7 @@ VSDepthOutput MainVS(VSInput IN) OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy; OUT.m_uv[1] = IN.m_uv1; - if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset) + if(ShouldHandleParallaxInDepthShaders()) { OUT.m_worldPosition = worldPosition.xyz; @@ -74,45 +73,28 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) { PSDepthOutput OUT; - // Clip Alpha - float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex]; - float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex]; - float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source); - CheckClipping(alpha, MaterialSrg::m_opacityFactor); - OUT.m_depth = IN.m_position.z; - - if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset) + + if(ShouldHandleParallaxInDepthShaders()) { // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); - float3 tangent = tangents[MaterialSrg::m_parallaxUvIndex]; - float3 bitangent = bitangents[MaterialSrg::m_parallaxUvIndex]; - float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - - float3 tangentOffset = GetParallaxOffset( MaterialSrg::m_depthFactor, - IN.m_uv[MaterialSrg::m_parallaxUvIndex], - ViewSrg::m_worldPosition.xyz - IN.m_worldPosition, - tangent, - bitangent, - IN.m_normal, - uvMatrix); - - PixelDepthOffset pdo = CalcPixelDepthOffset(MaterialSrg::m_depthFactor, - tangentOffset, - IN.m_worldPosition, - tangent, - bitangent, - IN.m_normal, - uvMatrixInverse, - ObjectSrg::GetWorldMatrix(), - ViewSrg::m_viewProjectionMatrix); - OUT.m_depth = pdo.m_depth; + + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset, + ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, + IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth); } + + // Clip Alpha + float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex]; + float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex]; + float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source); + CheckClipping(alpha, MaterialSrg::m_opacityFactor); + return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl index 36c8e81a94..23e28e8742 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl @@ -40,8 +40,8 @@ COMMON_OPTIONS_NORMAL() COMMON_OPTIONS_CLEAR_COAT() COMMON_OPTIONS_OCCLUSION() COMMON_OPTIONS_EMISSIVE() -COMMON_OPTIONS_PARALLAX() COMMON_OPTIONS_DETAIL_MAPS() +// Note COMMON_OPTIONS_PARALLAX is in StandardPBR_Common.azsli because it's needed by all StandardPBR shaders. // Alpha #include "MaterialInputs/AlphaInput.azsli" @@ -102,8 +102,11 @@ VSOutput EnhancedPbr_ForwardPassVS(VSInput IN) // but we would need to address how it works with the parallax code below that indexes into the m_detailUV array. OUT.m_detailUv[0] = mul(MaterialSrg::m_detailUvMatrix, float3(IN.m_uv0, 1.0)).xy; OUT.m_detailUv[1] = mul(MaterialSrg::m_detailUvMatrix, float3(IN.m_uv1, 1.0)).xy; + + // Shadow coords will be calculated in the pixel shader in this case + bool skipShadowCoords = ShouldHandleParallax() && o_parallax_enablePixelDepthOffset; - VertexHelper(IN, OUT, worldPosition, o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset); + VertexHelper(IN, OUT, worldPosition, skipShadowCoords); return OUT; } @@ -132,9 +135,11 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // ------- Depth & Parallax ------- depth = IN.m_position.z; + + bool displacementIsClipped = false; // Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scatteirng is enabled - if(!o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useDepthMap) + if(ShouldHandleParallax()) { // GetParallaxInput applies an tangent offset to the UV. We want to apply the same offset to the detailUv (note: this needs to be tested with content) // The math is: offset = newUv - oldUv; detailUv += offset; @@ -143,9 +148,9 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset, ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, - IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth); + IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth, displacementIsClipped); // Apply second part of the offset to the detail UV (see comment above) IN.m_detailUv[MaterialSrg::m_parallaxUvIndex] -= IN.m_uv[MaterialSrg::m_parallaxUvIndex]; @@ -206,6 +211,11 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float3 baseColor = GetDetailedBaseColorInput( MaterialSrg::m_baseColorMap, MaterialSrg::m_sampler, baseColorUv, o_baseColor_useTexture, MaterialSrg::m_baseColor, MaterialSrg::m_baseColorFactor, o_baseColorTextureBlendMode, MaterialSrg::m_detail_baseColor_texture, MaterialSrg::m_sampler, detailUv, o_detail_baseColor_useTexture, detailLayerBaseColorFactor); + + if(o_parallax_highlightClipping && displacementIsClipped) + { + ApplyParallaxClippingHighlight(baseColor); + } // ------- Metallic ------- diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl index 15b58c4deb..80aedcd6f4 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl @@ -12,7 +12,6 @@ #include #include "EnhancedPBR_Common.azsli" -#include #include #include #include @@ -54,8 +53,8 @@ VertexOutput MainVS(VertexInput IN) // By design, only UV0 is allowed to apply transforms. OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy; OUT.m_uv[1] = IN.m_uv1; - - if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset) + + if(ShouldHandleParallaxInDepthShaders()) { OUT.m_worldPosition = worldPosition.xyz; @@ -75,57 +74,32 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) { PSDepthOutput OUT; + OUT.m_depth = IN.m_position.z; + + if(ShouldHandleParallaxInDepthShaders()) + { + static const float ShadowMapDepthBias = 0.000001; + + // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); + + float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); + float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); + + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset, + ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, + IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth); + + OUT.m_depth += ShadowMapDepthBias; + } + // Clip Alpha float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex]; float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex]; float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source); CheckClipping(alpha, MaterialSrg::m_opacityFactor); - OUT.m_depth = IN.m_position.z; - - float3 dirToCamera; - if(ViewSrg::m_projectionMatrix[0].w) - { - // orthographic projection (directional light) - // No view position, use light direction - dirToCamera = ViewSrg::m_viewMatrix[2].xyz; - } - else - { - dirToCamera = ViewSrg::m_worldPosition.xyz - IN.m_worldPosition; - } - - if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset) - { - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); - - float3 tangent = tangents[MaterialSrg::m_parallaxUvIndex]; - float3 bitangent = bitangents[MaterialSrg::m_parallaxUvIndex]; - - float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); - float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - - float3 tangentOffset = GetParallaxOffset( MaterialSrg::m_depthFactor, - IN.m_uv[MaterialSrg::m_parallaxUvIndex], - dirToCamera, - tangent, - bitangent, - IN.m_normal, - uvMatrix); - - PixelDepthOffset pdo = CalcPixelDepthOffset(MaterialSrg::m_depthFactor, - tangentOffset, - IN.m_worldPosition, - tangent, - bitangent, - IN.m_normal, - uvMatrixInverse, - ObjectSrg::GetWorldMatrix(), - ViewSrg::m_viewProjectionMatrix); - OUT.m_depth = pdo.m_depth; - } return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/MaterialInputs/ParallaxInput.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/MaterialInputs/ParallaxInput.azsli index edee4c3c07..ab601429e8 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/MaterialInputs/ParallaxInput.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/MaterialInputs/ParallaxInput.azsli @@ -24,16 +24,15 @@ #define COMMON_SRG_INPUTS_PARALLAX(prefix) \ Texture2D prefix##m_depthMap; \ float prefix##m_depthFactor; \ +float prefix##m_depthOffset; \ bool prefix##m_depthInverted; #define COMMON_OPTIONS_PARALLAX(prefix) \ option bool prefix##o_useDepthMap; -option bool o_parallax_feature_enabled; - -void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float depthFactor, +void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float depthFactor, float depthOffset, float4x4 objectWorldMatrix, float3x3 uvMatrix, float3x3 uvMatrixInverse, - inout float2 uv, inout float3 worldPosition, inout float depth) + inout float2 uv, inout float3 worldPosition, inout float depth, out bool isClipped) { if(o_parallax_feature_enabled) { @@ -49,20 +48,22 @@ void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float dep dirToCamera = ViewSrg::m_worldPosition.xyz - worldPosition; } - float3 tangentOffset = GetParallaxOffset( depthFactor, - uv, - dirToCamera, - tangent, - bitangent, - normal, - uvMatrix); + ParallaxOffset tangentOffset = GetParallaxOffset( depthFactor, + depthOffset, + uv, + dirToCamera, + tangent, + bitangent, + normal, + uvMatrix); - uv += tangentOffset.xy; + uv += tangentOffset.m_offsetTS.xy; + isClipped = tangentOffset.m_isClipped; if(o_parallax_enablePixelDepthOffset) { PixelDepthOffset pdo = CalcPixelDepthOffset(depthFactor, - tangentOffset, + tangentOffset.m_offsetTS, worldPosition, tangent, bitangent, @@ -70,9 +71,20 @@ void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float dep uvMatrixInverse, objectWorldMatrix, ViewSrg::m_viewProjectionMatrix); + depth = pdo.m_depth; + worldPosition = pdo.m_worldPosition; } + } } +void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float depthFactor, float depthOffset, + float4x4 objectWorldMatrix, float3x3 uvMatrix, float3x3 uvMatrixInverse, + inout float2 uv, inout float3 worldPosition, inout float depth) +{ + bool isClipped; + GetParallaxInput(normal, tangent, bitangent, depthFactor, depthOffset, objectWorldMatrix, uvMatrix, uvMatrixInverse, uv, worldPosition, depth, isClipped); +} + diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype index 7610a4c9db..e2119dcf12 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype @@ -18,11 +18,6 @@ "displayName": "Parallax Settings", "description": "Properties for configuring the parallax effect, applied to all layers." }, - { - "id": "opacity", - "displayName": "Opacity", - "description": "Properties for configuring the materials transparency." - }, { "id": "uv", "displayName": "UVs", @@ -361,19 +356,6 @@ "id": "m_parallaxUvIndex" } }, - { - "id": "factor", - "displayName": "Factor", - "description": "Strength factor for scaling the depth values for all layers.", - "type": "Float", - "defaultValue": 1.0, - "min": 0.0, - "softMax": 2.0, - "connection": { - "type": "ShaderInput", - "id": "m_parallaxMainDepthFactor" - } - }, { "id": "algorithm", "displayName": "Algorithm", @@ -408,73 +390,17 @@ "type": "ShaderOption", "id": "o_parallax_enablePixelDepthOffset" } - } - ], - "opacity": [ + }, { - "id": "mode", - "displayName": "Opacity Mode", - "description": "Opacity mode for this texture.", - "type": "Enum", - "enumValues": [ "Opaque", "Cutout", "Blended" ], - "defaultValue": "Opaque", + "id": "showClipping", + "displayName": "Show Clipping", + "description": "Highlight areas where the heightmap is clipped by the mesh surface.", + "type": "Bool", + "defaultValue": false, "connection": { "type": "ShaderOption", - "id": "o_opacity_mode" + "id": "o_parallax_highlightClipping" } - }, - { - "id": "alphaSource", - "displayName": "Alpha Source", - "description": "Source texture of alpha value.", - "type": "Enum", - "enumValues": [ "Packed", "Split", "None" ], - "defaultValue": "Packed", - "connection": { - "type": "ShaderOption", - "id": "o_opacity_source" - } - }, - { - "id": "textureMap", - "displayName": "Texture Map", - "description": "Texture map for defining surface opacity.", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_opacityMap" - } - }, - { - "id": "textureMapUv", - "displayName": "UV", - "description": "Opacity texture map UV set", - "type": "Enum", - "enumIsUv": true, - "defaultValue": "Tiled", - "connection": { - "type": "ShaderInput", - "id": "m_opacityMapUvIndex" - } - }, - { - "id": "factor", - "displayName": "Factor", - "description": "Factor for cutout threshold and blending", - "type": "Float", - "min": 0.0, - "max": 1.0, - "defaultValue": 0.5, - "connection": { - "type": "ShaderInput", - "id": "m_opacityFactor" - } - }, - { - "id": "doubleSided", - "displayName": "Double-sided", - "description": "Whether to render back-faces or just front-faces.", - "type": "Bool" } ], "uv": [ @@ -1343,8 +1269,8 @@ }, { "id": "factor", - "displayName": "Factor", - "description": "Strength factor for scaling the depth values", + "displayName": "Heightmap Scale", + "description": "The total height of the heightmap in local model units.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -1354,6 +1280,19 @@ "id": "m_layer1_m_depthFactor" } }, + { + "id": "offset", + "displayName": "Offset", + "description": "Adjusts the overall displacement amount in local model units.", + "type": "Float", + "defaultValue": 0.0, + "softMin": -0.1, + "softMax": 0.1, + "connection": { + "type": "ShaderInput", + "id": "m_layer1_m_depthOffset" + } + }, { "id": "invert", "displayName": "Invert", @@ -2036,8 +1975,8 @@ }, { "id": "factor", - "displayName": "Factor", - "description": "Strength factor for scaling the depth values", + "displayName": "Heightmap Scale", + "description": "The total height of the heightmap in local model units.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -2047,6 +1986,19 @@ "id": "m_layer2_m_depthFactor" } }, + { + "id": "offset", + "displayName": "Offset", + "description": "Adjusts the overall displacement amount in local model units.", + "type": "Float", + "defaultValue": 0.0, + "softMin": -0.1, + "softMax": 0.1, + "connection": { + "type": "ShaderInput", + "id": "m_layer2_m_depthOffset" + } + }, { "id": "invert", "displayName": "Invert", @@ -2729,8 +2681,8 @@ }, { "id": "factor", - "displayName": "Factor", - "description": "Strength factor for scaling the depth values", + "displayName": "Heightmap Scale", + "description": "The total height of the heightmap in local model units.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -2740,6 +2692,19 @@ "id": "m_layer3_m_depthFactor" } }, + { + "id": "offset", + "displayName": "Offset", + "description": "Adjusts the overall displacement amount in local model units.", + "type": "Float", + "defaultValue": 0.0, + "softMin": -0.1, + "softMax": 0.1, + "connection": { + "type": "ShaderInput", + "id": "m_layer3_m_depthOffset" + } + }, { "id": "invert", "displayName": "Invert", @@ -2851,16 +2816,7 @@ { "file": "Shaders/MotionVector/SkinnedMeshMotionVector.shader", "tag": "SkinnedMeshMotionVector" - }, - // Used by the light culling system to produce accurate depth bounds for this object when it uses blended transparency - { - "file": "Shaders/Depth/DepthPassTransparentMin.shader", - "tag": "DepthPassTransparentMin" - }, - { - "file": "Shaders/Depth/DepthPassTransparentMax.shader", - "tag": "DepthPassTransparentMax" - } + } ], "functors": [ //############################################################################################## @@ -2885,7 +2841,7 @@ { "type": "Lua", "args": { - "file": "StandardPBR_ShaderEnable.lua" + "file": "StandardMultilayerPBR_ShaderEnable.lua" } }, { @@ -2925,27 +2881,6 @@ "file": "StandardPBR_SubsurfaceState.lua" } }, - { - "type": "Lua", - "args": { - "file": "StandardPBR_HandleOpacityDoubleSided.lua" - } - }, - { - "type": "OverrideDrawList", - "args": { - "triggerProperty": "opacity.mode", - "triggerValue": "Blended", - "shaderIndex": 1, - "drawList": "transparent" - } - }, - { - "type": "Lua", - "args": { - "file": "StandardPBR_HandleOpacityMode.lua" - } - }, //############################################################################################## // Layer 1 Functors //############################################################################################## diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli index db3ec45e0c..ba0eaf2ac1 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli @@ -14,6 +14,7 @@ #include #include +#include #include "MaterialInputs/BaseColorInput.azsli" #include "MaterialInputs/RoughnessInput.azsli" @@ -26,6 +27,8 @@ #include "MaterialInputs/ParallaxInput.azsli" #include "MaterialInputs/UvSetCount.azsli" +// ------ ShaderResourceGroup ---------------------------------------- + #define DEFINE_LAYER_SRG_INPUTS(prefix) \ COMMON_SRG_INPUTS_BASE_COLOR(prefix) \ COMMON_SRG_INPUTS_ROUGHNESS(prefix) \ @@ -57,17 +60,16 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial float4 m_pad3; // [GFX TODO][ATOM-14595] This is a workaround for a data stomping bug. Remove once it's fixed. uint m_parallaxUvIndex; - float m_parallaxMainDepthFactor; + + // These are used to limit the heightmap intersection search range to the narrowest band possible, to give the best quality result. + float m_displacementMin; // The lowest displacement value possible from all layers combined + float m_displacementMax; // The highest displacement value possible from all layers combined float3x3 m_uvMatrix; float4 m_pad4; // [GFX TODO][ATOM-14595] This is a workaround for a data stomping bug. Remove once it's fixed. float3x3 m_uvMatrixInverse; float4 m_pad5; // [GFX TODO][ATOM-14595] This is a workaround for a data stomping bug. Remove once it's fixed. - float m_opacityFactor; - Texture2D m_opacityMap; - uint m_opacityMapUvIndex; - Sampler m_sampler { AddressU = Wrap; @@ -109,6 +111,8 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial uint m_transmissionThicknessMapUvIndex; } +// ------ Shader Options ---------------------------------------- + enum class DebugDrawMode { None, BlendMaskValues, DepthMaps }; option DebugDrawMode o_debugDrawMode; @@ -121,6 +125,8 @@ option BlendMaskSource o_blendSource; // [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream. option bool o_blendMask_isBound; +// ------ Blend Utilities ---------------------------------------- + //! Returns the BlendMaskSource that will actually be used when rendering (not necessarily the same BlendMaskSource specified by the user) BlendMaskSource GetFinalBlendMaskSource() { @@ -181,6 +187,22 @@ float3 BlendLayers(float3 layer1, float3 layer2, float3 layer3, float3 blendMask return layer1 * blendMaskValues.r + layer2 * blendMaskValues.g + layer3 * blendMaskValues.b; } +// ------ Parallax Utilities ---------------------------------------- + +bool ShouldHandleParallax() +{ + // Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scattering is enabled. + // Also, all the debug draw modes avoid parallax (they early-return before parallax code actually) so you can see exactly where the various maps appear on the surface UV space. + return !o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_debugDrawMode == DebugDrawMode::None; +} + +bool ShouldHandleParallaxInDepthShaders() +{ + // The depth pass shaders need to calculate parallax when the result could affect the depth buffer (or when + // parallax could affect texel clipping but we don't have alpha/clipping support in multilayer PBR). + return ShouldHandleParallax() && o_parallax_enablePixelDepthOffset; +} + // These static values are used to pass extra data to the GetDepth callback function during the parallax depth search. static float3 s_blendMaskFromVertexStream; @@ -192,7 +214,7 @@ void GetDepth_Setup(float3 vertexBlendMask) } // Callback function for ParallaxMapping.azsli -float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) +DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) { float3 layerDepthValues = float3(0,0,0); @@ -204,8 +226,9 @@ float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) layerUv = mul(MaterialSrg::m_layer1_m_uvMatrix, float3(uv, 1.0)).xy; } - layerDepthValues.r = SampleDepthOrHeightMap(MaterialSrg::m_layer1_m_depthInverted, MaterialSrg::m_layer1_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy); + layerDepthValues.r = SampleDepthOrHeightMap(MaterialSrg::m_layer1_m_depthInverted, MaterialSrg::m_layer1_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; layerDepthValues.r *= MaterialSrg::m_layer1_m_depthFactor; + layerDepthValues.r -= MaterialSrg::m_layer1_m_depthOffset; } if(o_layer2_o_useDepthMap) @@ -216,8 +239,9 @@ float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) layerUv = mul(MaterialSrg::m_layer2_m_uvMatrix, float3(uv, 1.0)).xy; } - layerDepthValues.g = SampleDepthOrHeightMap(MaterialSrg::m_layer2_m_depthInverted, MaterialSrg::m_layer2_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy); + layerDepthValues.g = SampleDepthOrHeightMap(MaterialSrg::m_layer2_m_depthInverted, MaterialSrg::m_layer2_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; layerDepthValues.g *= MaterialSrg::m_layer2_m_depthFactor; + layerDepthValues.g -= MaterialSrg::m_layer2_m_depthOffset; } if(o_layer3_o_useDepthMap) @@ -228,8 +252,9 @@ float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) layerUv = mul(MaterialSrg::m_layer3_m_uvMatrix, float3(uv, 1.0)).xy; } - layerDepthValues.b = SampleDepthOrHeightMap(MaterialSrg::m_layer3_m_depthInverted, MaterialSrg::m_layer3_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy); + layerDepthValues.b = SampleDepthOrHeightMap(MaterialSrg::m_layer3_m_depthInverted, MaterialSrg::m_layer3_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth; layerDepthValues.b *= MaterialSrg::m_layer3_m_depthFactor; + layerDepthValues.b -= MaterialSrg::m_layer3_m_depthOffset; } // Note, when the blend source is BlendMaskSource::VertexColors, parallax will not be able to blend correctly between layers. It will end up using the same blend mask values @@ -237,7 +262,6 @@ float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) // you have a small depth factor relative to the size of the blend transition. float3 blendMaskValues = GetBlendMaskValues(uv, s_blendMaskFromVertexStream); - float3 depth = BlendLayers(layerDepthValues.r, layerDepthValues.g, layerDepthValues.b, blendMaskValues); - - return depth; + float depth = BlendLayers(layerDepthValues.r, layerDepthValues.g, layerDepthValues.b, blendMaskValues); + return DepthResultAbsolute(depth); } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl index 074b9dce26..ae156d7313 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl @@ -11,12 +11,10 @@ */ #include -#include #include #include #include -#include "MaterialInputs/AlphaInput.azsli" #include "MaterialInputs/ParallaxInput.azsli" @@ -72,7 +70,7 @@ VSDepthOutput MainVS(VSInput IN) OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy; OUT.m_uv[1] = IN.m_uv1; - if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset) + if(ShouldHandleParallaxInDepthShaders()) { OUT.m_worldPosition = worldPosition.xyz; @@ -101,18 +99,9 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) { PSDepthOutput OUT; - // Alpha - float2 layer1_baseColorUV = IN.m_uv[MaterialSrg::m_layer1_m_baseColorMapUvIndex]; - float2 layer2_baseColorUV = IN.m_uv[MaterialSrg::m_layer2_m_baseColorMapUvIndex]; - float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex]; - // [GFX TODO][ATOM-14589] Figure out how to deal with opacity, instead of just hard-coding to layer1 - float alpha = SampleAlpha(MaterialSrg::m_layer1_m_baseColorMap, MaterialSrg::m_opacityMap, layer1_baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source); - - CheckClipping(alpha, MaterialSrg::m_opacityFactor); - OUT.m_depth = IN.m_position.z; - if(o_debugDrawMode == DebugDrawMode::None && o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset) + if(ShouldHandleParallaxInDepthShaders()) { // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; @@ -126,7 +115,9 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_parallaxMainDepthFactor, + float parallaxOverallOffset = MaterialSrg::m_displacementMax; + float parallaxOverallFactor = MaterialSrg::m_displacementMax - MaterialSrg::m_displacementMin; + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], parallaxOverallFactor, parallaxOverallOffset, ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl index 560c7ab7eb..a83ea629e4 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl @@ -55,7 +55,6 @@ DEFINE_LAYER_OPTIONS(o_layer1_) DEFINE_LAYER_OPTIONS(o_layer2_) DEFINE_LAYER_OPTIONS(o_layer3_) -#include "MaterialInputs/AlphaInput.azsli" #include "MaterialInputs/SubsurfaceInput.azsli" #include "MaterialInputs/TransmissionInput.azsli" #include "StandardMultilayerPBR_Common.azsli" @@ -121,9 +120,8 @@ VSOutput ForwardPassVS(VSInput IN) OUT.m_blendMask = float3(1,1,1); } - // We can skip per-vertex shadow coords when parallax is enabled because we need to calculate per-pixel shadow coords anyway. - // We cannot skip shadow coords when o_debugDrawMode is on because some debug draw modes return before parallax. - bool skipShadowCoords = o_debugDrawMode == DebugDrawMode::None && o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset; + // Shadow coords will be calculated in the pixel shader in this case + bool skipShadowCoords = ShouldHandleParallax() && o_parallax_enablePixelDepthOffset; VertexHelper(IN, OUT, worldPosition, skipShadowCoords); return OUT; @@ -167,23 +165,27 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float if(o_debugDrawMode == DebugDrawMode::DepthMaps) { GetDepth_Setup(IN.m_blendMask); - float depth = GetDepth(IN.m_uv[MaterialSrg::m_parallaxUvIndex], float2(0,0), float2(0,0)); + float depth = GetNormalizedDepth(-MaterialSrg::m_displacementMax, -MaterialSrg::m_displacementMin, IN.m_uv[MaterialSrg::m_parallaxUvIndex], float2(0,0), float2(0,0)); return DebugOutput(float3(depth,depth,depth)); } // ------- Parallax ------- + bool displacementIsClipped = false; + // Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scatteirng is enabled - if(!o_enableSubsurfaceScattering && o_parallax_feature_enabled) + if(ShouldHandleParallax()) { GetDepth_Setup(IN.m_blendMask); float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - - GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_parallaxMainDepthFactor, + + float parallaxOverallOffset = MaterialSrg::m_displacementMax; + float parallaxOverallFactor = MaterialSrg::m_displacementMax - MaterialSrg::m_displacementMin; + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], parallaxOverallFactor, parallaxOverallOffset, ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, - IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth); + IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth, displacementIsClipped); // Adjust directional light shadow coorinates for parallax correction if(o_parallax_enablePixelDepthOffset) @@ -218,15 +220,6 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // Now that any parallax has been calculated, we calculate the blend factors for any layers that are impacted by the parallax. float3 blendMaskValues = GetBlendMaskValues(IN.m_uv[MaterialSrg::m_blendMaskUvIndex], IN.m_blendMask); - // ------- Alpha & Clip ------- - - float2 layer1_baseColorUv = uvLayer1[MaterialSrg::m_layer1_m_baseColorMapUvIndex]; - float2 layer2_baseColorUv = uvLayer2[MaterialSrg::m_layer2_m_baseColorMapUvIndex]; - float2 layer3_baseColorUv = uvLayer3[MaterialSrg::m_layer3_m_baseColorMapUvIndex]; - float2 opacityUv = IN.m_uv[MaterialSrg::m_opacityMapUvIndex]; - // [GFX TODO][ATOM-14589] Figure out how to deal with opacity, instead of just hard-coding to layer1 - float alpha = GetAlphaInputAndClip(MaterialSrg::m_layer1_m_baseColorMap, MaterialSrg::m_opacityMap, layer1_baseColorUv, opacityUv, MaterialSrg::m_sampler, MaterialSrg::m_opacityFactor, o_opacity_source); - // ------- Normal ------- float3 layer1_normalFactor = MaterialSrg::m_layer1_m_normalFactor * blendMaskValues.r; @@ -245,6 +238,10 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float surface.normal = normalize(TangentSpaceToWorld(normalTS, IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex])); // ------- Base Color ------- + + float2 layer1_baseColorUv = uvLayer1[MaterialSrg::m_layer1_m_baseColorMapUvIndex]; + float2 layer2_baseColorUv = uvLayer2[MaterialSrg::m_layer2_m_baseColorMapUvIndex]; + float2 layer3_baseColorUv = uvLayer3[MaterialSrg::m_layer3_m_baseColorMapUvIndex]; float3 layer1_sampledColor = GetBaseColorInput(MaterialSrg::m_layer1_m_baseColorMap, MaterialSrg::m_sampler, layer1_baseColorUv, MaterialSrg::m_layer1_m_baseColor.rgb, o_layer1_o_baseColor_useTexture); float3 layer2_sampledColor = GetBaseColorInput(MaterialSrg::m_layer2_m_baseColorMap, MaterialSrg::m_sampler, layer2_baseColorUv, MaterialSrg::m_layer2_m_baseColor.rgb, o_layer2_o_baseColor_useTexture); @@ -253,6 +250,11 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float3 layer2_baseColor = BlendBaseColor(layer2_sampledColor, MaterialSrg::m_layer2_m_baseColor.rgb, MaterialSrg::m_layer2_m_baseColorFactor, o_layer2_o_baseColorTextureBlendMode, o_layer2_o_baseColor_useTexture); float3 layer3_baseColor = BlendBaseColor(layer3_sampledColor, MaterialSrg::m_layer3_m_baseColor.rgb, MaterialSrg::m_layer3_m_baseColorFactor, o_layer3_o_baseColorTextureBlendMode, o_layer3_o_baseColor_useTexture); float3 baseColor = BlendLayers(layer1_baseColor, layer2_baseColor, layer3_baseColor, blendMaskValues); + + if(o_parallax_highlightClipping && displacementIsClipped) + { + ApplyParallaxClippingHighlight(baseColor); + } // ------- Metallic ------- @@ -427,32 +429,13 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float lightingData.FinalizeLighting(surface.transmission.tint); - if (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent) - { - alpha = FresnelSchlickWithRoughness(lightingData.NdotV, alpha, surface.roughnessLinear).x; // Increase opacity at grazing angles. - } + const float alpha = 1.0; PbrLightingOutput lightingOutput = GetPbrLightingOutput(surface, lightingData, alpha); - // ------- Opacity ------- - - if (o_opacity_mode == OpacityMode::Blended) - { - // [GFX_TODO ATOM-13187] PbrLighting shouldn't be writing directly to render targets. It's confusing when - // specular is being added to diffuse just because we're calling render target 0 "diffuse". - - // For blended mode, we do (dest * alpha) + (source * 1.0). This allows the specular - // to be added on top of the diffuse, but then the diffuse must be pre-multiplied. - // It's done this way because surface transparency doesn't really change specular response (eg, glass). - lightingOutput.m_diffuseColor.rgb *= lightingOutput.m_diffuseColor.w; // pre-multiply diffuse - lightingOutput.m_diffuseColor.rgb += lightingOutput.m_specularColor.rgb; // add specular - } - else - { - // Pack factor and quality, drawback: because of precision limit of float16 cannot represent exact 1, maximum representable value is 0.9961 - uint factorAndQuality = dot(round(float2(saturate(surfaceScatteringFactor), MaterialSrg::m_subsurfaceScatteringQuality) * 255), float2(256, 1)); - lightingOutput.m_diffuseColor.w = factorAndQuality * (o_enableSubsurfaceScattering ? 1.0 : -1.0); - } + // Pack factor and quality, drawback: because of precision limit of float16 cannot represent exact 1, maximum representable value is 0.9961 + uint factorAndQuality = dot(round(float2(saturate(surfaceScatteringFactor), MaterialSrg::m_subsurfaceScatteringQuality) * 255), float2(256, 1)); + lightingOutput.m_diffuseColor.w = factorAndQuality * (o_enableSubsurfaceScattering ? 1.0 : -1.0); return lightingOutput; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Parallax.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Parallax.lua index 522121c96f..8880a4b842 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Parallax.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Parallax.lua @@ -20,10 +20,12 @@ function GetMaterialPropertyDependencies() "layer1_parallax.enable", "layer2_parallax.enable", "layer3_parallax.enable", - "parallax.factor", "layer1_parallax.factor", "layer2_parallax.factor", - "layer3_parallax.factor" + "layer3_parallax.factor", + "layer1_parallax.offset", + "layer2_parallax.offset", + "layer3_parallax.offset" } end @@ -31,6 +33,23 @@ function GetShaderOptionDependencies() return {"o_parallax_feature_enabled"} end +function MergeRange(heightMinMax, offset, factor) + top = offset + bottom = offset - factor + + if(heightMinMax[1] == nil) then + heightMinMax[1] = top + else + heightMinMax[1] = math.max(heightMinMax[1], top) + end + + if(heightMinMax[0] == nil) then + heightMinMax[0] = bottom + else + heightMinMax[0] = math.min(heightMinMax[0], bottom) + end +end + function Process(context) local enableParallax = context:GetMaterialPropertyValue_bool("parallax.enable") local enable1 = context:GetMaterialPropertyValue_bool("layer1_parallax.enable") @@ -39,30 +58,25 @@ function Process(context) enableParallax = enableParallax and (enable1 or enable2 or enable3) context:SetShaderOptionValue_bool("o_parallax_feature_enabled", enableParallax) - -- Smaller values for the main parallax factor used in GetParallaxOffset() give better quality. - -- So increase the per-layer parallax factors by normalizing them, and reduce the main factor accordingly. if(enableParallax) then local factorLayer1 = context:GetMaterialPropertyValue_float("layer1_parallax.factor") local factorLayer2 = context:GetMaterialPropertyValue_float("layer2_parallax.factor") local factorLayer3 = context:GetMaterialPropertyValue_float("layer3_parallax.factor") - local mainFactor = context:GetMaterialPropertyValue_float("parallax.factor") - maxLayerFactor = 0.0 - if(enable1) then maxLayerFactor = math.max(maxLayerFactor, factorLayer1) end - if(enable2) then maxLayerFactor = math.max(maxLayerFactor, factorLayer2) end - if(enable3) then maxLayerFactor = math.max(maxLayerFactor, factorLayer3) end + local offsetLayer1 = context:GetMaterialPropertyValue_float("layer1_parallax.offset") + local offsetLayer2 = context:GetMaterialPropertyValue_float("layer2_parallax.offset") + local offsetLayer3 = context:GetMaterialPropertyValue_float("layer3_parallax.offset") - if(maxLayerFactor < 0.0001) then + local heightMinMax = {nil, nil} + if(enable1) then MergeRange(heightMinMax, offsetLayer1, factorLayer1) end + if(enable2) then MergeRange(heightMinMax, offsetLayer2, factorLayer2) end + if(enable3) then MergeRange(heightMinMax, offsetLayer3, factorLayer3) end + + if(heightMinMax[1] - heightMinMax[0] < 0.0001) then context:SetShaderOptionValue_bool("o_parallax_feature_enabled", false) else - factorLayer1 = factorLayer1 / maxLayerFactor - factorLayer2 = factorLayer2 / maxLayerFactor - factorLayer3 = factorLayer3 / maxLayerFactor - mainFactor = mainFactor * maxLayerFactor; - context:SetShaderConstant_float("m_layer1_m_depthFactor", factorLayer1) - context:SetShaderConstant_float("m_layer2_m_depthFactor", factorLayer2) - context:SetShaderConstant_float("m_layer3_m_depthFactor", factorLayer3) - context:SetShaderConstant_float("m_parallaxMainDepthFactor", mainFactor) + context:SetShaderConstant_float("m_displacementMin", heightMinMax[0]) + context:SetShaderConstant_float("m_displacementMax", heightMinMax[1]) end end end @@ -76,8 +90,8 @@ function ProcessEditor(context) end context:SetMaterialPropertyVisibility("parallax.parallaxUv", visibility) - context:SetMaterialPropertyVisibility("parallax.factor", visibility) context:SetMaterialPropertyVisibility("parallax.algorithm", visibility) context:SetMaterialPropertyVisibility("parallax.quality", visibility) context:SetMaterialPropertyVisibility("parallax.pdo", visibility) + context:SetMaterialPropertyVisibility("parallax.showClipping", visibility) end diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua index a1695e827b..119dfed436 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua @@ -42,7 +42,8 @@ function ProcessEditor(context) if(not enable or textureMap == nil) then visibility = MaterialPropertyVisibility_Hidden end - + context:SetMaterialPropertyVisibility("parallax.factor", visibility) + context:SetMaterialPropertyVisibility("parallax.offset", visibility) context:SetMaterialPropertyVisibility("parallax.invert", visibility) end diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ShaderEnable.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ShaderEnable.lua new file mode 100644 index 0000000000..69df610ab2 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ShaderEnable.lua @@ -0,0 +1,39 @@ +-------------------------------------------------------------------------------------- +-- +-- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +-- its licensors. +-- +-- For complete copyright and license terms please see the LICENSE at the root of this +-- distribution (the "License"). All use of this software is governed by the License, +-- or, if provided, by the license below or the license accompanying this file. Do not +-- remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- +-- +---------------------------------------------------------------------------------------------------- + +function GetMaterialPropertyDependencies() + return {"parallax.enable", "parallax.pdo"} +end + +function Process(context) + local parallaxEnabled = context:GetMaterialPropertyValue_bool("parallax.enable") + local parallaxPdoEnabled = context:GetMaterialPropertyValue_bool("parallax.pdo") + + local depthPass = context:GetShaderByTag("DepthPass") + local shadowMap = context:GetShaderByTag("Shadowmap") + local forwardPassEDS = context:GetShaderByTag("ForwardPass_EDS") + local depthPassWithPS = context:GetShaderByTag("DepthPass_WithPS") + local shadowMapWitPS = context:GetShaderByTag("Shadowmap_WithPS") + local forwardPass = context:GetShaderByTag("ForwardPass") + + local shadingAffectsDepth = parallaxEnabled and parallaxPdoEnabled; + + depthPass:SetEnabled(not shadingAffectsDepth) + shadowMap:SetEnabled(not shadingAffectsDepth) + forwardPassEDS:SetEnabled(not shadingAffectsDepth) + + depthPassWithPS:SetEnabled(shadingAffectsDepth) + shadowMapWitPS:SetEnabled(shadingAffectsDepth) + forwardPass:SetEnabled(shadingAffectsDepth) +end diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl index 1274e07f7d..325937b228 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl @@ -12,12 +12,10 @@ #include #include -#include #include #include #include -#include "MaterialInputs/AlphaInput.azsli" #include "MaterialInputs/ParallaxInput.azsli" #include "MaterialInputs/ParallaxInput.azsli" @@ -71,7 +69,7 @@ VertexOutput MainVS(VertexInput IN) OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy; OUT.m_uv[1] = IN.m_uv1; - if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset) + if(ShouldHandleParallaxInDepthShaders()) { OUT.m_worldPosition = worldPosition.xyz; @@ -100,18 +98,9 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) { PSDepthOutput OUT; - // Alpha - float2 layer1_baseColorUV = IN.m_uv[MaterialSrg::m_layer1_m_baseColorMapUvIndex]; - float2 layer2_baseColorUV = IN.m_uv[MaterialSrg::m_layer2_m_baseColorMapUvIndex]; - float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex]; - // [GFX TODO][ATOM-14589] Figure out how to deal with opacity, instead of just hard-coding to layer1 - float alpha = SampleAlpha(MaterialSrg::m_layer1_m_baseColorMap, MaterialSrg::m_opacityMap, layer1_baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source); - - CheckClipping(alpha, MaterialSrg::m_opacityFactor); - OUT.m_depth = IN.m_position.z; - if(o_debugDrawMode == DebugDrawMode::None && o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset) + if(ShouldHandleParallaxInDepthShaders()) { // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; @@ -124,13 +113,15 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - - GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_parallaxMainDepthFactor, + + float parallaxOverallOffset = MaterialSrg::m_displacementMax; + float parallaxOverallFactor = MaterialSrg::m_displacementMax - MaterialSrg::m_displacementMin; + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], parallaxOverallFactor, parallaxOverallOffset, ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth); OUT.m_depth = depth; } - + return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype index e071a793a5..a74ceb1783 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype @@ -910,8 +910,8 @@ }, { "id": "factor", - "displayName": "Factor", - "description": "Strength factor for scaling the depth values", + "displayName": "Heightmap Scale", + "description": "The total height of the heightmap in local model units.", "type": "Float", "defaultValue": 0.0, "min": 0.0, @@ -921,6 +921,19 @@ "id": "m_depthFactor" } }, + { + "id": "offset", + "displayName": "Offset", + "description": "Adjusts the overall displacement amount in local model units.", + "type": "Float", + "defaultValue": 0.0, + "softMin": -0.1, + "softMax": 0.1, + "connection": { + "type": "ShaderInput", + "id": "m_depthOffset" + } + }, { "id": "invert", "displayName": "Invert", @@ -966,6 +979,17 @@ "type": "ShaderOption", "id": "o_parallax_enablePixelDepthOffset" } + }, + { + "id": "showClipping", + "displayName": "Show Clipping", + "description": "Highlight areas where the heightmap is clipped by the mesh surface.", + "type": "Bool", + "defaultValue": false, + "connection": { + "type": "ShaderOption", + "id": "o_parallax_highlightClipping" + } } ], "subsurfaceScattering": [ diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Common.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Common.azsli index 1a7e039da3..5723a6cd1e 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Common.azsli @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include "MaterialInputs/BaseColorInput.azsli" #include "MaterialInputs/RoughnessInput.azsli" @@ -93,8 +95,23 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial } // Callback function for ParallaxMapping.azsli -float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) +DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) { return SampleDepthOrHeightMap(MaterialSrg::m_depthInverted, MaterialSrg::m_depthMap, MaterialSrg::m_sampler, uv, uv_ddx, uv_ddy); } + +COMMON_OPTIONS_PARALLAX() + +bool ShouldHandleParallax() +{ + // Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scattering is enabled. + return !o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useDepthMap; +} + +bool ShouldHandleParallaxInDepthShaders() +{ + // The depth pass shaders need to calculate parallax when the result could affect the depth buffer, or when + // parallax could affect texel clipping. + return ShouldHandleParallax() && (o_parallax_enablePixelDepthOffset || o_opacity_mode == OpacityMode::Cutout); +} diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl index 4d4f7b195a..28708c12d5 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl @@ -10,7 +10,6 @@ * */ -#include #include "./StandardPBR_Common.azsli" #include #include @@ -56,7 +55,7 @@ VSDepthOutput MainVS(VSInput IN) OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy; OUT.m_uv[1] = IN.m_uv1; - if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset) + if(ShouldHandleParallaxInDepthShaders()) { OUT.m_worldPosition = worldPosition.xyz; @@ -75,6 +74,23 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) { PSDepthOutput OUT; + OUT.m_depth = IN.m_position.z; + + if(ShouldHandleParallaxInDepthShaders()) + { + // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); + + float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); + float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); + + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset, + ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, + IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth); + } + // Alpha float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex]; float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex]; @@ -82,39 +98,5 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) CheckClipping(alpha, MaterialSrg::m_opacityFactor); - OUT.m_depth = IN.m_position.z; - - if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset) - { - // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. - float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) }; - float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); - - float3 tangent = tangents[MaterialSrg::m_parallaxUvIndex]; - float3 bitangent = bitangents[MaterialSrg::m_parallaxUvIndex]; - - float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); - float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - - float3 tangentOffset = GetParallaxOffset( MaterialSrg::m_depthFactor, - IN.m_uv[MaterialSrg::m_parallaxUvIndex], - ViewSrg::m_worldPosition.xyz - IN.m_worldPosition, - tangent, - bitangent, - IN.m_normal, - uvMatrix); - - PixelDepthOffset pdo = CalcPixelDepthOffset(MaterialSrg::m_depthFactor, - tangentOffset, - IN.m_worldPosition, - tangent, - bitangent, - IN.m_normal, - uvMatrixInverse, - ObjectSrg::GetWorldMatrix(), - ViewSrg::m_viewProjectionMatrix); - OUT.m_depth = pdo.m_depth; - } return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl index d3bc72d162..22751b49bc 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl @@ -40,7 +40,7 @@ COMMON_OPTIONS_NORMAL() COMMON_OPTIONS_CLEAR_COAT() COMMON_OPTIONS_OCCLUSION() COMMON_OPTIONS_EMISSIVE() -COMMON_OPTIONS_PARALLAX() +// Note COMMON_OPTIONS_PARALLAX is in StandardPBR_Common.azsli because it's needed by all StandardPBR shaders. // Alpha #include "MaterialInputs/AlphaInput.azsli" @@ -94,7 +94,10 @@ VSOutput StandardPbr_ForwardPassVS(VSInput IN) OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy; OUT.m_uv[1] = IN.m_uv1; - VertexHelper(IN, OUT, worldPosition, o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset); + // Shadow coords will be calculated in the pixel shader in this case + bool skipShadowCoords = ShouldHandleParallax() && o_parallax_enablePixelDepthOffset; + + VertexHelper(IN, OUT, worldPosition, skipShadowCoords); return OUT; } @@ -123,15 +126,18 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // ------- Depth & Parallax ------- depth = IN.m_position.z; + + bool displacementIsClipped = false; // Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scatteirng is enabled - if(!o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useDepthMap) + if(ShouldHandleParallax()) { + float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset, ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, - IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth); + IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth, displacementIsClipped); // Adjust directional light shadow coorinates for parallax correction if(o_parallax_enablePixelDepthOffset) @@ -166,6 +172,11 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float3 sampledColor = GetBaseColorInput(MaterialSrg::m_baseColorMap, MaterialSrg::m_sampler, baseColorUv, MaterialSrg::m_baseColor.rgb, o_baseColor_useTexture); float3 baseColor = BlendBaseColor(sampledColor, MaterialSrg::m_baseColor.rgb, MaterialSrg::m_baseColorFactor, o_baseColorTextureBlendMode, o_baseColor_useTexture); + if(o_parallax_highlightClipping && displacementIsClipped) + { + ApplyParallaxClippingHighlight(baseColor); + } + // ------- Metallic ------- float metallic = 0; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua index bf1b59616a..0287e1105e 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua @@ -41,8 +41,10 @@ function ProcessEditor(context) if(not enable or textureMap == nil) then visibility = MaterialPropertyVisibility_Hidden end - + context:SetMaterialPropertyVisibility("parallax.factor", visibility) + context:SetMaterialPropertyVisibility("parallax.offset", visibility) + context:SetMaterialPropertyVisibility("parallax.showClipping", visibility) context:SetMaterialPropertyVisibility("parallax.invert", visibility) context:SetMaterialPropertyVisibility("parallax.algorithm", visibility) context:SetMaterialPropertyVisibility("parallax.quality", visibility) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl index e792c25778..7f24b29700 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl @@ -12,7 +12,6 @@ #include #include "StandardPBR_Common.azsli" -#include #include #include #include @@ -56,7 +55,7 @@ VertexOutput MainVS(VertexInput IN) OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy; OUT.m_uv[1] = IN.m_uv1; - if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset) + if(ShouldHandleParallaxInDepthShaders()) { OUT.m_worldPosition = worldPosition.xyz; @@ -76,28 +75,9 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) { PSDepthOutput OUT; - // Alpha - float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex]; - float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex]; - float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source); - - CheckClipping(alpha, MaterialSrg::m_opacityFactor); - OUT.m_depth = IN.m_position.z; - float3 dirToCamera; - if(ViewSrg::m_projectionMatrix[0].w) - { - // orthographic projection (directional light) - // No view position, use light direction - dirToCamera = ViewSrg::m_viewMatrix[2].xyz; - } - else - { - dirToCamera = ViewSrg::m_worldPosition.xyz - IN.m_worldPosition; - } - - if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset) + if(ShouldHandleParallaxInDepthShaders()) { static const float ShadowMapDepthBias = 0.000001; @@ -106,31 +86,22 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); - float3 tangent = tangents[MaterialSrg::m_parallaxUvIndex]; - float3 bitangent = bitangents[MaterialSrg::m_parallaxUvIndex]; - float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); - float3 tangentOffset = GetParallaxOffset( MaterialSrg::m_depthFactor, - IN.m_uv[MaterialSrg::m_parallaxUvIndex], - dirToCamera, - tangent, - bitangent, - IN.m_normal, - uvMatrix); + GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset, + ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse, + IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth); - PixelDepthOffset pdo = CalcPixelDepthOffset(MaterialSrg::m_depthFactor, - tangentOffset, - IN.m_worldPosition, - tangent, - bitangent, - IN.m_normal, - uvMatrixInverse, - ObjectSrg::GetWorldMatrix(), - ViewSrg::m_viewProjectionMatrix); - - OUT.m_depth = pdo.m_depth + ShadowMapDepthBias; + OUT.m_depth += ShadowMapDepthBias; } + + // Alpha + float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex]; + float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex]; + float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source); + + CheckClipping(alpha, MaterialSrg::m_opacityFactor); + return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ParallaxMapping.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ParallaxMapping.azsli index 8a6b5c479a..1d2beb0f10 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ParallaxMapping.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ParallaxMapping.azsli @@ -18,39 +18,147 @@ option bool o_parallax_enablePixelDepthOffset; option enum class ParallaxAlgorithm {Basic, Steep, POM, Relief, Contact} o_parallax_algorithm; option enum class ParallaxQuality {Low, Medium, High, Ultra} o_parallax_quality; + +option bool o_parallax_feature_enabled; +option bool o_parallax_highlightClipping; option bool o_parallax_shadow; +// I tried to make this an enum class, but ran into some DXC bug when compiling to SPIRV. +enum DepthResultCode +{ + DepthResultCode_Invalid, + DepthResultCode_Normalized, //!< The result is in range [0,1], where 0 is the top of the heightmap and 1 is the bottom of the heightmap. + DepthResultCode_Absolute //!< The result is tangent space units (the same as world units if there's no mesh scaling), where 0 is at the mesh surface and positive values are below the surface. +}; + +//! The return value for the GetDepth() callback function below. +struct DepthResult +{ + DepthResultCode m_resultCode; + float m_depth; +}; + +//! Convenience function for making a DepthResult with Code::Normalized +DepthResult DepthResultNormalized(float depth) +{ + DepthResult result; + result.m_resultCode = DepthResultCode_Normalized; + result.m_depth = depth; + return result; +} + +//! Convenience function for making a DepthResult with Code::Absolute +DepthResult DepthResultAbsolute(float depth) +{ + DepthResult result; + result.m_resultCode = DepthResultCode_Absolute; + result.m_depth = depth; + return result; +} + //! The client shader must define this function. //! This allows the client shader to implement special depth map sampling, for example procedurally generating or blending depth maps. +//! In simple cases though, the implementation of GetDepth() can simply call SampleDepthOrHeightMap(). //! @param uv the UV coordinates to use for sampling //! @param uv_ddx will be set to ddx_fine(uv) //! @param uv_ddy will be set to ddy_fine(uv) -float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy); +//! @return see struct DepthResult +DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy); //! Convenience function that can be used to implement GetDepth(). //! @param isHeightmap indicates whether to sample the map is a height map rather than a depth map. -float SampleDepthOrHeightMap(bool isHeightmap, Texture2D map, sampler mapSampler, float2 uv, float2 uv_ddx, float2 uv_ddy) +//! @return see struct DepthResult. In this case it will always contain a Code::Normalized result. +DepthResult SampleDepthOrHeightMap(bool isHeightmap, Texture2D map, sampler mapSampler, float2 uv, float2 uv_ddx, float2 uv_ddy) { - return abs((isHeightmap * 1.0) - map.SampleGrad(mapSampler, uv, uv_ddx, uv_ddy).r); + DepthResult result; + result.m_resultCode = DepthResultCode_Normalized; + result.m_depth = abs((isHeightmap * 1.0) - map.SampleGrad(mapSampler, uv, uv_ddx, uv_ddy).r); + return result; } +//! Calls GetDepth() and then normalizes the result if it isn't normalized already. +//! @param startDepth is the high point, which corresponds to a normalized depth value of 0. +//! @param stopDepth is the low point, which corresponds to a normalized depth value of 1. +//! @param inverseDepthRange is an optimization, and must be set to "1.0 / (stopDepth - startDepth)". +//! @param uv the UV coordinates to use for sampling +//! @param uv_ddx must be set to ddx_fine(uv) +//! @param uv_ddy must be set to ddy_fine(uv) +//! @param a depth value in the range [0,1] +float GetNormalizedDepth(float startDepth, float stopDepth, float inverseDepthRange, float2 uv, float2 uv_ddx, float2 uv_ddy) +{ + // startDepth can be less than 0, representing a displacement above the mesh surface. + // But since we don't currently support any vertex displacement, negative depth values would cause various + // problems especially when PDO is enabled, like parallax surfaces clipping through foreground geometry, and parallax + // surfaces disappearing at low angles. So we clamp all depth values to a minimum of 0. + + float normalizedDepth = 0.0; + + DepthResult depthResult = GetDepth(uv, uv_ddx, uv_ddy); + + if(stopDepth - startDepth > 0.0001) + { + if(DepthResultCode_Normalized == depthResult.m_resultCode) + { + float minNormalizedDepth = -startDepth * inverseDepthRange; + normalizedDepth = max(depthResult.m_depth, minNormalizedDepth); + } + else if(DepthResultCode_Absolute == depthResult.m_resultCode) + { + float clampedAbsoluteDepth = max(depthResult.m_depth, 0.0); + normalizedDepth = (clampedAbsoluteDepth - startDepth) * inverseDepthRange; + } + } + + return normalizedDepth; +} + +float GetNormalizedDepth(float startDepth, float stopDepth, float2 uv, float2 uv_ddx, float2 uv_ddy) +{ + float inverseDepthRange = 1.0 / (stopDepth - startDepth); + return GetNormalizedDepth(startDepth, stopDepth, inverseDepthRange, uv, uv_ddx, uv_ddy); +} + +void ApplyParallaxClippingHighlight(inout float3 baseColor) +{ + baseColor = lerp(baseColor, float3(1.0, 0.0, 1.0), 0.5); +} + +struct ParallaxOffset +{ + float3 m_offsetTS; //!< represents the intersection point relative to the geometry surface, in tangent space. + bool m_isClipped; //!< Indicates whether the result is being clipped by the geometry surface, mainly for debug rendering. Only set when o_parallax_highlightClipping is true. +}; + // dirToCameraTS should be in tangent space and normalized // From Reat-Time Rendering 3rd edition, p.192 -float3 BasicParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraTS) +ParallaxOffset BasicParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraTS) { // the amount to shift - float2 delta = dirToCameraTS.xy * GetDepth(uv, ddx_fine(uv), ddy_fine(uv)) * depthFactor; + float2 delta = dirToCameraTS.xy * GetNormalizedDepth(0, depthFactor, uv, ddx_fine(uv), ddy_fine(uv)) * depthFactor; - float3 offset = float3(0,0,0); - offset.xy -= delta; - return offset; + ParallaxOffset result; + + result.m_offsetTS = float3(0,0,0); + result.m_offsetTS.xy -= delta; + result.m_isClipped = false; + return result; } -// dirToCameraTS and dirToLightTS should be in tangent space and normalized -// Adapt from CryEngine shader shadelib.cfi and POM function in https://github.com/a-riccardi/shader-toy +// Performs ray intersection against a surface with a heightmap. +// Adapted from CryEngine shader shadelib.cfi and POM function in https://github.com/a-riccardi/shader-toy // check https://github.com/UPBGE/blender/issues/1009 for more details. -float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraTS, float3 dirToLightTS, int numSteps, inout float parallaxShadowAttenuation) +// @param depthFactor - scales the heightmap in tangent space units (which normally ends up being world units). +// @param depthOffset - offsets the heighmap up or down in tangent space units (which normally ends up being world units). +// @param uv - the UV coordinates on the surface, where the search will begin, used to sample the heightmap. +// @param dirToCameraTS - normalized direction to the camera, in tangent space. +// @param dirToLightTS - normalized direction to a light source, in tangent space, for self-shadowing (if enabled via o_parallax_shadow). +// @param numSteps - the number of steps to take when marching along the ray searching for intersection. +// @param parallaxShadowAttenuation - returns a factor for attenuating a light source, for self-shadowing (if enabled via o_parallax_shadow). +ParallaxOffset AdvancedParallaxMapping(float depthFactor, float depthOffset, float2 uv, float3 dirToCameraTS, float3 dirToLightTS, int numSteps, inout float parallaxShadowAttenuation) { + ParallaxOffset result; + result.m_isClipped = false; + float dirToCameraZInverse = 1.0 / dirToCameraTS.z; float step = 1.0 / numSteps; float currentStep = 0.0; @@ -61,21 +169,38 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT float2 ddx_uv = ddx_fine(uv); float2 ddy_uv = ddy_fine(uv); - float currentSample = GetDepth(uv, ddx_uv, ddy_uv); - float prevSample; - float3 parallaxOffset = float3(0,0,0); + float depthSearchStart = -depthOffset; + float depthSearchEnd = depthSearchStart + depthFactor; + + float inverseDepthFactor = 1.0 / depthFactor; - // find the intersect step + // This is the relative position at which we begin searching for intersection. + // It is adjusted according to the depthOffset, raising or lowering the whole surface by depthOffset units. + float3 parallaxOffset = dirToCameraTS.xyz * dirToCameraZInverse * depthOffset; + + // Get an initial heightmap sample to start the intersection search, starting at our initial parallaxOffset position. + float currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, uv + parallaxOffset.xy, ddx_uv, ddy_uv); + float prevSample; + + // Note that when depthOffset < 0, we could actually narrow the search so that instead of going through the entire [depthSearchStart,depthSearchEnd] range + // of the heightmap, we could go through the range [0,depthSearchEnd]. This would give more accurate results and fewer artifacts + // in case where the magnitude of depthOffset is significant. But for the sake of simplicity we currently search the whole range in all cases. + + // Do a basic search for the intersect step while(currentSample > currentStep) { currentStep += step; parallaxOffset += delta; + prevSample = currentSample; - currentSample = GetDepth(uv + parallaxOffset.xy, ddx_uv, ddy_uv); + currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, uv + parallaxOffset.xy, ddx_uv, ddy_uv); } + // Depending on the algorithm, we refine the result of the above search switch(o_parallax_algorithm) { + case ParallaxAlgorithm::Steep: + break; // This algorithm just relies on the course intersection test loop above case ParallaxAlgorithm::POM: { if(currentStep > 0.0) @@ -108,7 +233,7 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT parallaxOffset += reliefDelta * depthSign; currentStep += reliefStep * depthSign; - currentSample = GetDepth(uv + parallaxOffset.xy, ddx_uv, ddy_uv); + currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, uv + parallaxOffset.xy, ddx_uv, ddy_uv); } } break; @@ -136,7 +261,7 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT parallaxOffset += adjustedDelta; prevSample = currentSample; - currentSample = GetDepth(uv + parallaxOffset.xy, ddx_uv, ddy_uv); + currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, uv + parallaxOffset.xy, ddx_uv, ddy_uv); } } break; @@ -144,6 +269,30 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT default: break; } + + // Even though we do a bunch of clamping above when calling GetClampedDepth(), there are still cases where the parallax offset + // can be noticeably above the surface and still needs to be clamped here. The main case is when depthFactor==0 and depthOffset>1. + if(parallaxOffset.z > 0.0) + { + parallaxOffset = float3(0,0,0); + } + + if (o_parallax_highlightClipping) + { + // The most accurate way to report clipping is to sample the heightmap one last time at the final adjusted UV. + // (trying to do it based on parallaxOffset.z values just leads to too many edge cases) + + DepthResult depthResult = GetDepth(uv + parallaxOffset.xy, ddx_uv, ddy_uv); + + if(DepthResultCode_Normalized == depthResult.m_resultCode) + { + result.m_isClipped = lerp(depthSearchStart, depthSearchEnd, depthResult.m_depth) < 0; + } + else if(DepthResultCode_Absolute == depthResult.m_resultCode) + { + result.m_isClipped = depthResult.m_depth < 0.0; + } + } if(o_parallax_shadow && any(dirToLightTS)) { @@ -168,7 +317,7 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT } shadowUV += shadowDelta; - currentSample = GetDepth(shadowUV, ddx_uv, ddy_uv); + currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, shadowUV, ddx_uv, ddy_uv); currentStep -= step; } @@ -181,12 +330,13 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT parallaxShadowAttenuation = 1; } } - - return parallaxOffset; + + result.m_offsetTS = parallaxOffset; + return result; } // return offset in tangent space -float3 CalculateParallaxOffset(float depthFactor, float2 uv, float3 dirToCameraTS, float3 dirToLightTS, inout float parallaxShadowAttenuation) +ParallaxOffset CalculateParallaxOffset(float depthFactor, float depthOffset, float2 uv, float3 dirToCameraTS, float3 dirToLightTS, inout float parallaxShadowAttenuation) { if(o_parallax_algorithm == ParallaxAlgorithm::Basic) { @@ -194,27 +344,34 @@ float3 CalculateParallaxOffset(float depthFactor, float2 uv, float3 dirToCameraT } else { - float3 parallaxOffset; + ParallaxOffset parallaxOffset; switch(o_parallax_quality) { case ParallaxQuality::Low: - parallaxOffset = AdvancedParallaxMapping(depthFactor, uv, dirToCameraTS, dirToLightTS, 16, parallaxShadowAttenuation); + parallaxOffset = AdvancedParallaxMapping(depthFactor, depthOffset, uv, dirToCameraTS, dirToLightTS, 16, parallaxShadowAttenuation); break; case ParallaxQuality::Medium: - parallaxOffset = AdvancedParallaxMapping(depthFactor, uv, dirToCameraTS, dirToLightTS, 32, parallaxShadowAttenuation); + parallaxOffset = AdvancedParallaxMapping(depthFactor, depthOffset, uv, dirToCameraTS, dirToLightTS, 32, parallaxShadowAttenuation); break; case ParallaxQuality::High: - parallaxOffset = AdvancedParallaxMapping(depthFactor, uv, dirToCameraTS, dirToLightTS, 64, parallaxShadowAttenuation); + parallaxOffset = AdvancedParallaxMapping(depthFactor, depthOffset, uv, dirToCameraTS, dirToLightTS, 64, parallaxShadowAttenuation); break; case ParallaxQuality::Ultra: - parallaxOffset = AdvancedParallaxMapping(depthFactor, uv, dirToCameraTS, dirToLightTS, 128, parallaxShadowAttenuation); + parallaxOffset = AdvancedParallaxMapping(depthFactor, depthOffset, uv, dirToCameraTS, dirToLightTS, 128, parallaxShadowAttenuation); break; } return parallaxOffset; } } -float3 GetParallaxOffset( float depthFactor, +// Performs ray intersection against a surface with a heightmap, to determine an offset amount required for a parallax effect. +// @param depthFactor - scales the heightmap in tangent space units (which normally ends up being world units). +// @param depthOffset - offsets the heighmap up or down in tangent space units (which normally ends up being world units). +// @param uv - the UV coordinates on the surface, where the search will begin, used to sample the heightmap. +// @param dirToCameraTS - normalized direction to the camera, in tangent space. +// @param dirToLightTS - normalized direction to a light source, in tangent space, for self-shadowing (if enabled via o_parallax_shadow). +ParallaxOffset GetParallaxOffset( float depthFactor, + float depthOffset, float2 uv, float3 dirToCameraWS, float3 tangentWS, @@ -236,7 +393,7 @@ float3 GetParallaxOffset( float depthFactor, float4 dirToCameraTransformed = mul(uv3DTransform, float4(dirToCameraTS, 0.0)); float dummy = 1; - return CalculateParallaxOffset(depthFactor, uv, normalize(dirToCameraTransformed.xyz), float3(0,0,0), dummy); + return CalculateParallaxOffset(depthFactor, depthOffset, uv, normalize(dirToCameraTransformed.xyz), float3(0,0,0), dummy); } struct PixelDepthOffset diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Decals/DecalFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Decals/DecalFeatureProcessorInterface.h index 214feb2040..a7aebf5aa4 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Decals/DecalFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Decals/DecalFeatureProcessorInterface.h @@ -88,7 +88,10 @@ namespace AZ //! Sets the transform of the decal //! Equivalent to calling SetDecalPosition() + SetDecalOrientation() + SetDecalHalfSize() + //! @{ virtual void SetDecalTransform(DecalHandle handle, const AZ::Transform& world) = 0; + virtual void SetDecalTransform(DecalHandle handle, const AZ::Transform& world, const AZ::Vector3& nonUniformScale) = 0; + //! @} //! Sets the material information for this decal virtual void SetDecalMaterial(DecalHandle handle, const AZ::Data::AssetId) = 0; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index 60bf487de3..27bac09a63 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -306,7 +306,7 @@ namespace AZ for (const RPI::ViewPtr& view : packet.m_views) { if (m_renderPipelineIdsForPersistentView.find(view.get()) != m_renderPipelineIdsForPersistentView.end() && - (view->GetUsageFlags() & RPI::View::UsageCamera)) + (RHI::CheckBitsAny(view->GetUsageFlags(), RPI::View::UsageCamera | RPI::View::UsageReflectiveCubeMap))) { RPI::ShaderResourceGroup* viewSrg = view->GetShaderResourceGroup().get(); diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp index 7abc6698fa..55fa633e5d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp @@ -264,7 +264,12 @@ namespace AZ void DecalFeatureProcessor::SetDecalTransform(DecalHandle handle, const AZ::Transform& world) { - // https://jira.agscollab.com/browse/ATOM-4330 + SetDecalTransform(handle, world, AZ::Vector3::CreateOne()); + } + + void DecalFeatureProcessor::SetDecalTransform(DecalHandle handle, const AZ::Transform& world, const AZ::Vector3& nonUniformScale) + { + // ATOM-4330 // Original Open 3D Engine uploads a 4x4 matrix rather than quaternion, rotation, scale. // That is more memory but less calculation because it is doing a matrix inverse rather than a polar decomposition // I've done some experiments and uploading a 3x4 transform matrix with 3x3 matrix inverse should be possible @@ -274,7 +279,7 @@ namespace AZ if (handle.IsValid()) { Quaternion orientation = world.GetRotation(); - Vector3 scale = world.GetScale(); + Vector3 scale = world.GetScale() * nonUniformScale; SetDecalHalfSize(handle, scale); SetDecalPosition(handle, world.GetTranslation()); diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.h index 9c4acf6322..3b99715d5a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.h @@ -73,7 +73,10 @@ namespace AZ //! Sets the transform of the decal //! Equivalent to calling SetDecalPosition() + SetDecalOrientation() + SetDecalHalfSize() + //! @{ void SetDecalTransform(DecalHandle handle, const AZ::Transform& world) override; + void SetDecalTransform(DecalHandle handle, const AZ::Transform& world, const AZ::Vector3& nonUniformScale) override; + //! @} //! Sets the material information for this decal void SetDecalMaterial(DecalHandle handle, const AZ::Data::AssetId) override; diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp index 9d1ea4e680..4000df646d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp @@ -270,10 +270,16 @@ namespace AZ } void DecalTextureArrayFeatureProcessor::SetDecalTransform(DecalHandle handle, const AZ::Transform& world) + { + SetDecalTransform(handle, world, AZ::Vector3::CreateOne()); + } + + void DecalTextureArrayFeatureProcessor::SetDecalTransform(DecalHandle handle, const AZ::Transform& world, + const AZ::Vector3& nonUniformScale) { if (handle.IsValid()) { - SetDecalHalfSize(handle, world.GetScale()); + SetDecalHalfSize(handle, nonUniformScale * world.GetScale()); SetDecalPosition(handle, world.GetTranslation()); SetDecalOrientation(handle, world.GetRotation()); diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h index 7ab073c7f9..825e461fc2 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h @@ -82,7 +82,10 @@ namespace AZ //! Sets the transform of the decal //! Equivalent to calling SetDecalPosition() + SetDecalOrientation() + SetDecalHalfSize() + //! @{ void SetDecalTransform(const DecalHandle handle, const AZ::Transform& world) override; + void SetDecalTransform(const DecalHandle handle, const AZ::Transform& world, const AZ::Vector3& nonUniformScale) override; + //! @} //! Sets the material information for this decal void SetDecalMaterial(const DecalHandle handle, const AZ::Data::AssetId id) override; diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp index f56a753fb6..b22f4b5861 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp @@ -69,9 +69,8 @@ namespace AZ if (m_shouldUpdatePassParameters) { - auto* passSystem = AZ::RPI::PassSystemInterface::Get(); - UpdateEyeAdaptationPass(passSystem); - UpdateLuminanceHeatmap(passSystem); + UpdateEyeAdaptationPass(); + UpdateLuminanceHeatmap(); m_shouldUpdatePassParameters = false; } @@ -140,7 +139,8 @@ namespace AZ if (m_heatmapEnabled != value) { m_heatmapEnabled = value; - m_shouldUpdatePassParameters = true; + // Update immediately so that the ExposureControlSettings can just be turned off and killed without having to wait for another Simulate() call + UpdateLuminanceHeatmap(); } } @@ -198,8 +198,10 @@ namespace AZ } } - void ExposureControlSettings::UpdateEyeAdaptationPass(RPI::PassSystemInterface* passSystem) + void ExposureControlSettings::UpdateEyeAdaptationPass() { + auto* passSystem = AZ::RPI::PassSystemInterface::Get(); + // [GFX-TODO][ATOM-13224] Remove UpdateLuminanceHeatmap and UpdateEyeAdaptationPass auto passTemplateName = m_eyeAdaptationPassTemplateNameId; @@ -220,8 +222,10 @@ namespace AZ } } - void ExposureControlSettings::UpdateLuminanceHeatmap(RPI::PassSystemInterface* passSystem) + void ExposureControlSettings::UpdateLuminanceHeatmap() { + auto* passSystem = AZ::RPI::PassSystemInterface::Get(); + // [GFX-TODO][ATOM-13194] Support multiple views for the luminance heatmap // [GFX-TODO][ATOM-13224] Remove UpdateLuminanceHeatmap and UpdateEyeAdaptationPass const RPI::Ptr luminanceHeatmap = passSystem->GetRootPass()->FindPassByNameRecursive(m_luminanceHeatmapNameId); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.h b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.h index ebf5a1fe01..566f60dd28 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.h @@ -84,8 +84,8 @@ namespace AZ void UpdateExposureControlRelatedPassParameters(); - void UpdateLuminanceHeatmap(RPI::PassSystemInterface* passSystem); - void UpdateEyeAdaptationPass(RPI::PassSystemInterface* passSystem); + void UpdateLuminanceHeatmap(); + void UpdateEyeAdaptationPass(); PostProcessSettings* m_parentSettings = nullptr; bool m_shouldUpdatePassParameters = true; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialFunctorSourceDataSerializer.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialFunctorSourceDataSerializer.h index 59f129b52a..a10e773891 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialFunctorSourceDataSerializer.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialFunctorSourceDataSerializer.h @@ -33,6 +33,8 @@ namespace AZ JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) override; + private: + BaseJsonSerializer::OperationFlags GetOperationsFlags() const override; }; } // namespace RPI diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialFunctorSourceDataSerializer.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialFunctorSourceDataSerializer.cpp index a31d97c913..7ac8240faa 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialFunctorSourceDataSerializer.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialFunctorSourceDataSerializer.cpp @@ -127,5 +127,10 @@ namespace AZ return context.Report(result, "Successfully processed MaterialFunctorSourceData."); } + + BaseJsonSerializer::OperationFlags JsonMaterialFunctorSourceDataSerializer::GetOperationsFlags() const + { + return OperationFlags::ManualDefault; + } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM_Cutout.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM_Cutout.material new file mode 100644 index 0000000000..fb862dc5d3 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/012_Parallax_POM_Cutout.material @@ -0,0 +1,26 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardPBR.materialtype", + "parentMaterial": "", + "propertyLayoutVersion": 3, + "properties": { + "baseColor": { + "textureMap": "TestData/Textures/TextureHaven/4k_castle_brick_02_red/4k_castle_brick_02_red_bc.png" + }, + "opacity": { + "alphaSource": "Split", + "mode": "Cutout", + "textureMap": "TestData/Textures/checker8x8_512.png" + }, + "parallax": { + "algorithm": "POM", + "enable": true, + "factor": 0.10000000149011612, + "quality": "High", + "textureMap": "TestData/Textures/TextureHaven/4k_castle_brick_02_red/4k_castle_brick_02_red_disp.png" + }, + "uv": { + "scale": 0.5 + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.azsl b/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.azsl index 793b71b3af..424de7749d 100644 --- a/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.azsl +++ b/Gems/Atom/TestData/TestData/Materials/Types/AutoBrick_ForwardPass.azsl @@ -121,12 +121,12 @@ void GetSurfaceShape(float2 uv, out float depth, out float3 normal) } // Callback function for ParallaxMapping.azsli -float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) +DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) { float depth; float3 normal; GetSurfaceShape(uv, depth, normal); - return depth; + return DepthResultNormalized(depth); } ForwardPassOutput AutoBrick_ForwardPassPS(VSOutput IN) @@ -136,15 +136,18 @@ ForwardPassOutput AutoBrick_ForwardPassPS(VSOutput IN) 0,1,0, 0,0,1 }; - float3 tangentOffset = GetParallaxOffset( AutoBrickSrg::m_lineDepth, - IN.m_uv, - ViewSrg::m_worldPosition.xyz - IN.m_worldPosition, - IN.m_tangent, - IN.m_bitangent, - IN.m_normal, - identityUvMatrix); + float depthOffset = 0.0; + + ParallaxOffset tangentOffset = GetParallaxOffset( AutoBrickSrg::m_lineDepth, + depthOffset, + IN.m_uv, + ViewSrg::m_worldPosition.xyz - IN.m_worldPosition, + IN.m_tangent, + IN.m_bitangent, + IN.m_normal, + identityUvMatrix); - IN.m_uv += tangentOffset.xy; + IN.m_uv += tangentOffset.m_offsetTS.xy; float3 baseColor = float3(1,1,1); const float noise = AutoBrickSrg::m_noise.Sample(AutoBrickSrg::m_sampler, IN.m_uv).r; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorGroupHeaderWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorGroupHeaderWidget.h index d1beed1a65..bba299187b 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorGroupHeaderWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorGroupHeaderWidget.h @@ -15,6 +15,7 @@ #if !defined(Q_MOC_RUN) #include #include +#include #include #endif @@ -31,7 +32,11 @@ namespace AtomToolsFramework void SetExpanded(bool expanded); bool IsExpanded() const; + Q_SIGNALS: + void clicked(QMouseEvent* event); + protected: + void mousePressEvent(QMouseEvent* event) override; void paintEvent(QPaintEvent* event) override; private: diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h index 5f16894a0c..81b512faf2 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h @@ -55,6 +55,12 @@ namespace AtomToolsFramework //! Calls Rebuild for all InspectorGroupWidget, allowing for destructive UI changes virtual void RebuildAll() = 0; + + //! Expands all groups and headers + virtual void ExpandAll() = 0; + + //! Collapses all groups and headers + virtual void CollapseAll() = 0; }; using InspectorRequestBus = AZ::EBus; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h index 9f8df2da71..b0932b3b04 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h @@ -29,11 +29,8 @@ namespace Ui namespace AtomToolsFramework { - class InspectorPropertyGroupWidget; -} + class InspectorGroupHeaderWidget; -namespace AtomToolsFramework -{ //! Provides controls for viewing and editing object settings. //! The settings can be divided into groups, with each one showing a subset of properties. class InspectorWidget @@ -66,8 +63,15 @@ namespace AtomToolsFramework void RefreshAll() override; void RebuildAll() override; + void ExpandAll() override; + void CollapseAll() override; + private: + void OnHeaderClicked(QMouseEvent* event, InspectorGroupHeaderWidget* groupHeader, QWidget* groupWidget); + QVBoxLayout* m_layout = nullptr; QScopedPointer m_ui; + AZStd::vector m_headers; + AZStd::vector m_groups; }; } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h index 8e64c1467e..ff2a9cdf98 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -99,11 +99,15 @@ namespace AtomToolsFramework AZStd::optional ViewportScreenToWorldRay( const AzFramework::ScreenPoint& screenPosition) override; + //! Set interface for providing viewport specific settings (e.g. snapping properties). + void SetViewportSettings(AzToolsFramework::ViewportInteraction::ViewportSettings* viewportSettings); + // AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Handler ... void BeginCursorCapture() override; void EndCursorCapture() override; AzFramework::ScreenPoint ViewportCursorScreenPosition() override; AZStd::optional PreviousViewportCursorScreenPosition() override; + bool IsMouseOver() const override; // AzFramework::WindowRequestBus::Handler ... void SetWindowTitle(const AZStd::string& title) override; @@ -155,5 +159,7 @@ namespace AtomToolsFramework bool m_capturingCursor = false; // The last known position of the mouse cursor, if one is available. AZStd::optional m_lastCursorPosition; + // The viewport settings (e.g. grid snapping, grid size) for this viewport. + const AzToolsFramework::ViewportInteraction::ViewportSettings* m_viewportSettings = nullptr; }; } //namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorGroupHeaderWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorGroupHeaderWidget.cpp index 2928d745c1..41cc1e4a50 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorGroupHeaderWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorGroupHeaderWidget.cpp @@ -14,10 +14,10 @@ #include #include -#include -#include #include +#include #include +#include #include namespace AtomToolsFramework @@ -44,6 +44,11 @@ namespace AtomToolsFramework return m_expanded; } + void InspectorGroupHeaderWidget::mousePressEvent(QMouseEvent* event) + { + emit clicked(event); + } + void InspectorGroupHeaderWidget::paintEvent([[maybe_unused]] QPaintEvent* event) { QPainter painter(this); @@ -52,19 +57,10 @@ namespace AtomToolsFramework auto& icon = m_expanded ? m_iconExpanded : m_iconCollapsed; const QRect iconRect(5, (geometry().height() / 2) - (iconSize.height() / 2), iconSize.width(), iconSize.height()); - style->drawItemPixmap(&painter, - iconRect, - Qt::AlignLeft | Qt::AlignVCenter, - icon.scaledToWidth(iconSize.width())); + style->drawItemPixmap(&painter, iconRect, Qt::AlignLeft | Qt::AlignVCenter, icon.scaledToWidth(iconSize.width())); const auto textRect = QRect(25, 0, geometry().width() - 21, geometry().height()); - style->drawItemText(&painter, - textRect, - Qt::AlignLeft | Qt::AlignVCenter, - QPalette(), - true, - text(), - QPalette::HighlightedText); + style->drawItemText(&painter, textRect, Qt::AlignLeft | Qt::AlignVCenter, QPalette(), true, text(), QPalette::HighlightedText); } } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp index 31a291c845..ec5f9893bd 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp @@ -10,12 +10,13 @@ * */ +#include #include #include #include -#include #include +#include #include #include @@ -38,6 +39,8 @@ namespace AtomToolsFramework m_layout = new QVBoxLayout(m_ui->m_propertyContent); m_layout->setContentsMargins(0, 0, 0, 0); m_layout->setSpacing(0); + m_headers.clear(); + m_groups.clear(); } void InspectorWidget::AddGroupsBegin() @@ -52,8 +55,7 @@ namespace AtomToolsFramework m_layout->addStretch(); // Scroll to top whenever there is new content - m_ui->m_propertyScrollArea->verticalScrollBar()->setValue( - m_ui->m_propertyScrollArea->verticalScrollBar()->minimum()); + m_ui->m_propertyScrollArea->verticalScrollBar()->setValue(m_ui->m_propertyScrollArea->verticalScrollBar()->minimum()); setUpdatesEnabled(true); } @@ -68,15 +70,15 @@ namespace AtomToolsFramework groupHeader->setText(groupDisplayName.c_str()); groupHeader->setToolTip(groupDescription.c_str()); m_layout->addWidget(groupHeader); + m_headers.push_back(groupHeader); groupWidget->setObjectName(groupNameId.c_str()); groupWidget->setParent(m_ui->m_propertyContent); m_layout->addWidget(groupWidget); + m_groups.push_back(groupWidget); - connect(groupHeader, &AzQtComponents::ExtendedLabel::clicked, this, [groupHeader, groupWidget]() - { - groupHeader->SetExpanded(!groupHeader->IsExpanded()); - groupWidget->setVisible(groupHeader->IsExpanded()); + connect(groupHeader, &InspectorGroupHeaderWidget::clicked, this, [this, groupHeader, groupWidget](QMouseEvent* event) { + OnHeaderClicked(event, groupHeader, groupWidget); }); } @@ -111,6 +113,57 @@ namespace AtomToolsFramework groupWidget->Rebuild(); } } + + void InspectorWidget::ExpandAll() + { + for (auto headerWidget : m_headers) + { + headerWidget->SetExpanded(true); + } + for (auto groupWidget : m_groups) + { + groupWidget->setVisible(true); + } + } + + void InspectorWidget::CollapseAll() + { + for (auto headerWidget : m_headers) + { + headerWidget->SetExpanded(false); + } + for (auto groupWidget : m_groups) + { + groupWidget->setVisible(false); + } + } + + void InspectorWidget::OnHeaderClicked(QMouseEvent* event, InspectorGroupHeaderWidget* groupHeader, QWidget* groupWidget) + { + if (event->button() == Qt::MouseButton::LeftButton) + { + groupHeader->SetExpanded(!groupHeader->IsExpanded()); + groupWidget->setVisible(groupHeader->IsExpanded()); + return; + } + + if (event->button() == Qt::MouseButton::RightButton) + { + QMenu menu; + menu.addAction("Expand", [groupHeader, groupWidget]() { + groupHeader->SetExpanded(true); + groupWidget->setVisible(true); + })->setEnabled(!groupHeader->IsExpanded()); + menu.addAction("Collapse", [groupHeader, groupWidget]() { + groupHeader->SetExpanded(false); + groupWidget->setVisible(false); + })->setEnabled(groupHeader->IsExpanded()); + menu.addAction("Expand All", [this]() { ExpandAll(); }); + menu.addAction("Collapse All", [this]() { CollapseAll(); }); + menu.exec(event->globalPos()); + return; + } + } } // namespace AtomToolsFramework #include diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index 5f3c553601..961de670e8 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -384,27 +384,32 @@ namespace AtomToolsFramework bool RenderViewportWidget::GridSnappingEnabled() { - return false; + return m_viewportSettings ? m_viewportSettings->GridSnappingEnabled() : false; } float RenderViewportWidget::GridSize() { - return 0.0f; + return m_viewportSettings ? m_viewportSettings->GridSize() : 0.0f; } bool RenderViewportWidget::ShowGrid() { - return false; + return m_viewportSettings ? m_viewportSettings->ShowGrid() : false; } bool RenderViewportWidget::AngleSnappingEnabled() { - return false; + return m_viewportSettings ? m_viewportSettings->AngleSnappingEnabled() : false; } float RenderViewportWidget::AngleStep() { - return 0.0f; + return m_viewportSettings ? m_viewportSettings->AngleStep() : 0.0f; + } + + void RenderViewportWidget::SetViewportSettings(AzToolsFramework::ViewportInteraction::ViewportSettings* viewportSettings) + { + m_viewportSettings = viewportSettings; } AzFramework::ScreenPoint RenderViewportWidget::ViewportWorldToScreen(const AZ::Vector3& worldPosition) @@ -472,6 +477,11 @@ namespace AtomToolsFramework : AZStd::optional{}; } + bool RenderViewportWidget::IsMouseOver() const + { + return m_mouseOver; + } + void RenderViewportWidget::BeginCursorCapture() { if (m_capturingCursor) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp index 83ec5a41c5..36e4b76cec 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include @@ -136,6 +138,11 @@ namespace MaterialEditor const InputChannel::State state = event.m_inputChannel.GetState(); const KeyMask keysOld = m_keys; + bool mouseOver = false; + AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::EventResult( + mouseOver, GetViewportId(), + &AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Events::IsMouseOver); + if (!m_behavior) { EvaluateControlBehavior(); @@ -178,7 +185,10 @@ namespace MaterialEditor } else if (inputChannelId == InputDeviceMouse::Movement::Z) { - m_behavior->MoveZ(event.m_inputChannel.GetValue()); + if (mouseOver) + { + m_behavior->MoveZ(event.m_inputChannel.GetValue()); + } } break; case InputChannel::State::Ended: @@ -222,7 +232,10 @@ namespace MaterialEditor } else if (inputChannelId == InputDeviceMouse::Movement::Z) { - m_behavior->MoveZ(event.m_inputChannel.GetValue()); + if (mouseOver) + { + m_behavior->MoveZ(event.m_inputChannel.GetValue()); + } } break; } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorBrowserInteractions.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorBrowserInteractions.cpp index 92e02d5b42..9b79bcb6d9 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorBrowserInteractions.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorBrowserInteractions.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -248,6 +249,24 @@ namespace MaterialEditor } } }); + + menu->addSeparator(); + + QAction* createMaterialAction = menu->addAction(QObject::tr("Create Material...")); + QObject::connect(createMaterialAction, &QAction::triggered, caller, [caller, entry]() + { + CreateMaterialDialog createDialog(entry->GetFullPath().c_str(), caller); + createDialog.adjustSize(); + + if (createDialog.exec() == QDialog::Accepted && + !createDialog.m_materialFileInfo.absoluteFilePath().isEmpty() && + !createDialog.m_materialTypeFileInfo.absoluteFilePath().isEmpty()) + { + MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::CreateDocumentFromFile, + createDialog.m_materialTypeFileInfo.absoluteFilePath().toUtf8().constData(), + createDialog.m_materialFileInfo.absoluteFilePath().toUtf8().constData()); + } + }); } void MaterialEditorBrowserInteractions::AddPerforceMenuActions([[maybe_unused]] QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp b/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp index d36307e4ec..40684640d6 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp +++ b/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp @@ -101,6 +101,11 @@ AZ::RPI::WindowContextSharedPtr AZ::FFont::GetDefaultWindowContext() const bool AZ::FFont::InitFont(AZ::RPI::Scene* renderScene) { + if (!renderScene) + { + return false; + } + auto initializationState = InitializationState::Uninitialized; // Do an atomic transition to Initializing if we're in the Uninitialized state. // Otherwise, check the current state. @@ -111,11 +116,6 @@ bool AZ::FFont::InitFont(AZ::RPI::Scene* renderScene) return initializationState == InitializationState::Initialized; } - if (!renderScene) - { - return false; - } - // Create and initialize DynamicDrawContext for font draw AZ::RPI::Ptr dynamicDraw = m_atomFont->GetOrCreateDynamicDrawForScene(renderScene); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.cpp index 22a7e1c6c0..3c748e352e 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.cpp @@ -75,6 +75,12 @@ namespace AZ incompatible.push_back(AZ_CRC_CE("DecalService")); } + void DecalComponentController::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + dependent.push_back(AZ_CRC_CE("TransformService")); + dependent.push_back(AZ_CRC_CE("NonUniformScaleService")); + } + DecalComponentController::DecalComponentController(const DecalComponentConfig& config) : m_configuration(config) { @@ -90,6 +96,11 @@ namespace AZ m_handle = m_featureProcessor->AcquireDecal(); } + m_cachedNonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(m_cachedNonUniformScale, m_entityId, &AZ::NonUniformScaleRequests::GetScale); + AZ::NonUniformScaleRequestBus::Event(m_entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, + m_nonUniformScaleChangedHandler); + AZ::Transform local, world; AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::GetLocalAndWorld, local, world); OnTransformChanged(local, world); @@ -103,6 +114,7 @@ namespace AZ { DecalRequestBus::Handler::BusDisconnect(m_entityId); TransformNotificationBus::Handler::BusDisconnect(m_entityId); + m_nonUniformScaleChangedHandler.Disconnect(); if (m_featureProcessor) { m_featureProcessor->ReleaseDecal(m_handle); @@ -125,7 +137,18 @@ namespace AZ { if (m_featureProcessor) { - m_featureProcessor->SetDecalTransform(m_handle, world); + m_featureProcessor->SetDecalTransform(m_handle, world, m_cachedNonUniformScale); + } + } + + void DecalComponentController::HandleNonUniformScaleChange(const AZ::Vector3& nonUniformScale) + { + m_cachedNonUniformScale = nonUniformScale; + if (m_featureProcessor) + { + AZ::Transform world = AZ::Transform::CreateIdentity(); + AZ::TransformBus::EventResult(world, m_entityId, &AZ::TransformBus::Events::GetWorldTM); + m_featureProcessor->SetDecalTransform(m_handle, world, nonUniformScale); } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.h index dc6b6e70ea..14204c43dc 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.h @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,7 @@ namespace AZ static void Reflect(AZ::ReflectContext* context); static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); DecalComponentController() = default; DecalComponentController(const DecalComponentConfig& config); @@ -64,11 +66,18 @@ namespace AZ void OpacityChanged(); void SortKeyChanged(); void MaterialChanged(); + void HandleNonUniformScaleChange(const AZ::Vector3& nonUniformScale); DecalComponentConfig m_configuration; DecalFeatureProcessorInterface* m_featureProcessor = nullptr; DecalFeatureProcessorInterface::DecalHandle m_handle; EntityId m_entityId; + AZ::Vector3 m_cachedNonUniformScale = AZ::Vector3::CreateOne(); + + AZ::NonUniformScaleChangedEvent::Handler m_nonUniformScaleChangedHandler + { + [&](const AZ::Vector3& nonUniformScale) { HandleNonUniformScaleChange(nonUniformScale); } + }; }; } // namespace Render } // AZ namespace diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp index deab9d2715..70627fd2f1 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp @@ -131,6 +131,7 @@ namespace AZ void MeshComponentController::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) { dependent.push_back(AZ_CRC("TransformService", 0x8ee22c50)); + dependent.push_back(AZ_CRC_CE("NonUniformScaleService")); } void MeshComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ExposureControl/ExposureControlComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ExposureControl/ExposureControlComponentController.cpp index 3b73c65f96..967db514c6 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ExposureControl/ExposureControlComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ExposureControl/ExposureControlComponentController.cpp @@ -88,6 +88,9 @@ namespace AZ { ExposureControlRequestBus::Handler::BusDisconnect(m_entityId); + m_configuration.SetHeatmapEnabled(false); + OnConfigChanged(); + if (m_postProcessInterface) { m_postProcessInterface->RemoveExposureControlSettingsInterface(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp index 59d1f7afa7..772a995584 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp @@ -164,6 +164,7 @@ namespace AZ if (m_featureProcessor) { m_featureProcessor->RemoveProbe(m_handle); + m_handle = nullptr; } LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect(); diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp index 8452a6c690..3594802dab 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/ActorAsset.cpp @@ -486,10 +486,14 @@ namespace AZ AZ_Assert(jointIndicesBufferAsset->GetBufferDescriptor().m_byteCount == remappedJointIndexBufferSizeInBytes, "Joint indices data from EMotionFX is not the same size as the buffer from the model in '%s', lod '%d'", fullFileName.c_str(), lodIndex); AZ_Assert(skinWeightsBufferAsset->GetBufferDescriptor().m_byteCount == remappedSkinWeightsBufferSizeInBytes, "Skin weights data from EMotionFX is not the same size as the buffer from the model in '%s', lod '%d'", fullFileName.c_str(), lodIndex); - Data::Instance jointIndicesBuffer = RPI::Buffer::FindOrCreate(jointIndicesBufferAsset); - jointIndicesBuffer->UpdateData(blendIndexBufferData.data(), remappedJointIndexBufferSizeInBytes); - Data::Instance skinWeightsBuffer = RPI::Buffer::FindOrCreate(skinWeightsBufferAsset); - skinWeightsBuffer->UpdateData(blendWeightBufferData.data(), remappedSkinWeightsBufferSizeInBytes); + if (Data::Instance jointIndicesBuffer = RPI::Buffer::FindOrCreate(jointIndicesBufferAsset)) + { + jointIndicesBuffer->UpdateData(blendIndexBufferData.data(), remappedJointIndexBufferSizeInBytes); + } + if (Data::Instance skinWeightsBuffer = RPI::Buffer::FindOrCreate(skinWeightsBufferAsset)) + { + skinWeightsBuffer->UpdateData(blendWeightBufferData.data(), remappedSkinWeightsBufferSizeInBytes); + } } // Create read-only input assembly buffers that are not modified during skinning and shared across all instances diff --git a/Gems/Blast/Code/Include/Blast/BlastActor.h b/Gems/Blast/Code/Include/Blast/BlastActor.h index 4fb88b77ec..1c79eaef24 100644 --- a/Gems/Blast/Code/Include/Blast/BlastActor.h +++ b/Gems/Blast/Code/Include/Blast/BlastActor.h @@ -51,8 +51,8 @@ namespace Blast virtual AZ::Transform GetTransform() const = 0; virtual const BlastFamily& GetFamily() const = 0; virtual Nv::Blast::TkActor& GetTkActor() const = 0; - virtual AzPhysics::SimulatedBody* GetWorldBody() = 0; - virtual const AzPhysics::SimulatedBody* GetWorldBody() const = 0; + virtual AzPhysics::SimulatedBody* GetSimulatedBody() = 0; + virtual const AzPhysics::SimulatedBody* GetSimulatedBody() const = 0; virtual const AZ::Entity* GetEntity() const = 0; virtual const AZStd::vector& GetChunkIndices() const = 0; virtual bool IsStatic() const = 0; diff --git a/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp b/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp index c7c1c5a12e..1f05793b4b 100644 --- a/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp +++ b/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -164,7 +164,7 @@ namespace Blast AZ::Transform BlastActorImpl::GetTransform() const { - return GetWorldBody()->GetTransform(); + return GetSimulatedBody()->GetTransform(); } const BlastFamily& BlastActorImpl::GetFamily() const @@ -177,19 +177,19 @@ namespace Blast return m_tkActor; } - AzPhysics::SimulatedBody* BlastActorImpl::GetWorldBody() + AzPhysics::SimulatedBody* BlastActorImpl::GetSimulatedBody() { AzPhysics::SimulatedBody* worldBody = nullptr; - Physics::WorldBodyRequestBus::EventResult( - worldBody, m_entity->GetId(), &Physics::WorldBodyRequests::GetWorldBody); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult( + worldBody, m_entity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetSimulatedBody); return worldBody; } - const AzPhysics::SimulatedBody* BlastActorImpl::GetWorldBody() const + const AzPhysics::SimulatedBody* BlastActorImpl::GetSimulatedBody() const { AzPhysics::SimulatedBody* worldBody = nullptr; - Physics::WorldBodyRequestBus::EventResult( - worldBody, m_entity->GetId(), &Physics::WorldBodyRequests::GetWorldBody); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult( + worldBody, m_entity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetSimulatedBody); return worldBody; } diff --git a/Gems/Blast/Code/Source/Actor/BlastActorImpl.h b/Gems/Blast/Code/Source/Actor/BlastActorImpl.h index 3b9b77652a..3b686b3641 100644 --- a/Gems/Blast/Code/Source/Actor/BlastActorImpl.h +++ b/Gems/Blast/Code/Source/Actor/BlastActorImpl.h @@ -12,7 +12,6 @@ #pragma once #include -#include #include #include #include @@ -45,8 +44,8 @@ namespace Blast const AZStd::vector& GetChunkIndices() const override; bool IsStatic() const override; - AzPhysics::SimulatedBody* GetWorldBody() override; - const AzPhysics::SimulatedBody* GetWorldBody() const override; + AzPhysics::SimulatedBody* GetSimulatedBody() override; + const AzPhysics::SimulatedBody* GetSimulatedBody() const override; protected: //! We want to be able to override this function for testing purposes, because diff --git a/Gems/Blast/Code/Source/Actor/ShapesProvider.h b/Gems/Blast/Code/Source/Actor/ShapesProvider.h index 00a385a790..4e737be080 100644 --- a/Gems/Blast/Code/Source/Actor/ShapesProvider.h +++ b/Gems/Blast/Code/Source/Actor/ShapesProvider.h @@ -11,7 +11,6 @@ */ #pragma once -#include #include #include diff --git a/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp b/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp index 90bcb633bc..841163d2ab 100644 --- a/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp +++ b/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp @@ -467,7 +467,7 @@ namespace Blast } // transform all added lines from local to global - const AZ::Transform& localToGlobal = blastActor->GetWorldBody()->GetTransform(); + const AZ::Transform& localToGlobal = blastActor->GetSimulatedBody()->GetTransform(); for (uint32_t i = lineStartIndex; i < debugRenderBuffer.m_lines.size(); i++) { DebugLine& line = debugRenderBuffer.m_lines[i]; @@ -485,7 +485,7 @@ namespace Blast { for (auto actor : m_family->GetActorTracker().GetActors()) { - auto worldBody = actor->GetWorldBody(); + auto worldBody = actor->GetSimulatedBody(); if (actor->IsStatic()) { AZ::Vector3 gravity = AzPhysics::DefaultGravity; diff --git a/Gems/Blast/Code/Source/Family/ActorRenderManager.cpp b/Gems/Blast/Code/Source/Family/ActorRenderManager.cpp index 74bba48d3c..3695a9f07e 100644 --- a/Gems/Blast/Code/Source/Family/ActorRenderManager.cpp +++ b/Gems/Blast/Code/Source/Family/ActorRenderManager.cpp @@ -74,7 +74,7 @@ namespace Blast { if (m_chunkActors[chunkId]) { - m_meshFeatureProcessor->SetTransform(m_chunkMeshHandles[chunkId], m_chunkActors[chunkId]->GetWorldBody()->GetTransform(), m_scale); + m_meshFeatureProcessor->SetTransform(m_chunkMeshHandles[chunkId], m_chunkActors[chunkId]->GetSimulatedBody()->GetTransform(), m_scale); } } } diff --git a/Gems/Blast/Code/Source/Family/ActorTracker.cpp b/Gems/Blast/Code/Source/Family/ActorTracker.cpp index 5ceae87cd6..abcbdf5da3 100644 --- a/Gems/Blast/Code/Source/Family/ActorTracker.cpp +++ b/Gems/Blast/Code/Source/Family/ActorTracker.cpp @@ -22,12 +22,12 @@ namespace Blast { m_actors.emplace(actor); m_entityIdToActor.emplace(actor->GetEntity()->GetId(), actor); - m_bodyToActor.emplace(actor->GetWorldBody(), actor); + m_bodyToActor.emplace(actor->GetSimulatedBody(), actor); } void ActorTracker::RemoveActor(BlastActor* actor) { - m_bodyToActor.erase(actor->GetWorldBody()); + m_bodyToActor.erase(actor->GetSimulatedBody()); m_entityIdToActor.erase(actor->GetEntity()->GetId()); m_actors.erase(actor); } diff --git a/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp b/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp index 2b3f2fcc82..d276486548 100644 --- a/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp +++ b/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp @@ -179,7 +179,7 @@ namespace Blast { return; } - parentBody = parentActor->GetWorldBody(); + parentBody = parentActor->GetSimulatedBody(); const bool parentStatic = parentActor->IsStatic(); @@ -493,7 +493,7 @@ namespace Blast } // transform all added lines from local to global - AZ::Transform localToGlobal = blastActor->GetWorldBody()->GetTransform(); + AZ::Transform localToGlobal = blastActor->GetSimulatedBody()->GetTransform(); for (uint32_t i = lineStartIndex; i < debugRenderBuffer.m_lines.size(); i++) { DebugLine& line = debugRenderBuffer.m_lines[i]; diff --git a/Gems/Blast/Code/Source/Family/DamageManager.cpp b/Gems/Blast/Code/Source/Family/DamageManager.cpp index 1f275b99a6..f20d46cea3 100644 --- a/Gems/Blast/Code/Source/Family/DamageManager.cpp +++ b/Gems/Blast/Code/Source/Family/DamageManager.cpp @@ -124,7 +124,7 @@ namespace Blast AZ::Vector3 DamageManager::TransformToLocal(BlastActor& actor, const AZ::Vector3& globalPosition) { - const AZ::Transform hitToActorTransform(actor.GetWorldBody()->GetTransform().GetInverse()); + const AZ::Transform hitToActorTransform(actor.GetSimulatedBody()->GetTransform().GetInverse()); const AZ::Vector3 hitPos = hitToActorTransform.TransformPoint(globalPosition); return hitPos; } diff --git a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h index 95a16c311f..a1e57a6917 100644 --- a/Gems/Blast/Code/Tests/Mocks/BlastMocks.h +++ b/Gems/Blast/Code/Tests/Mocks/BlastMocks.h @@ -284,12 +284,12 @@ namespace Blast return m_transform; } - AzPhysics::SimulatedBody* GetWorldBody() override + AzPhysics::SimulatedBody* GetSimulatedBody() override { return m_worldBody.get(); } - const AzPhysics::SimulatedBody* GetWorldBody() const override + const AzPhysics::SimulatedBody* GetSimulatedBody() const override { return m_worldBody.get(); } diff --git a/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp b/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp index 4e1a3dc06e..38660fce02 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp @@ -543,7 +543,7 @@ namespace EditorPythonBindings { if (!oldPathSet.contains(thisStr)) { - pathAppend.append(AZStd::string::format("sys.path.append('%s')\n", thisStr.c_str())); + pathAppend.append(AZStd::string::format("sys.path.append(r'%s')\n", thisStr.c_str())); appended = true; } } diff --git a/Gems/ImageProcessing/Code/Source/Processing/ImageConvert.cpp b/Gems/ImageProcessing/Code/Source/Processing/ImageConvert.cpp index aec56af8d8..b5fc329a03 100644 --- a/Gems/ImageProcessing/Code/Source/Processing/ImageConvert.cpp +++ b/Gems/ImageProcessing/Code/Source/Processing/ImageConvert.cpp @@ -739,7 +739,9 @@ namespace ImageProcessing if (preset == nullptr) { - AZ_Assert(false, "preset should always exist"); + AZStd::string uuidStr; + textureSettings.m_preset.ToString(uuidStr); + AZ_Assert(false, "%s cannot find image preset with ID %s.", imageFilePath.c_str(), uuidStr.c_str()); return nullptr; } diff --git a/Gems/LandscapeCanvas/Code/Source/Editor/MainWindow.cpp b/Gems/LandscapeCanvas/Code/Source/Editor/MainWindow.cpp index 64d908bdb2..3879cd2597 100644 --- a/Gems/LandscapeCanvas/Code/Source/Editor/MainWindow.cpp +++ b/Gems/LandscapeCanvas/Code/Source/Editor/MainWindow.cpp @@ -206,7 +206,7 @@ namespace LandscapeCanvasEditor static const QStringList preferredCategories = { "Vegetation", - "Rendering" + "Atom" }; // There are a couple of cases where we prefer certain categories of Components diff --git a/Gems/LmbrCentral/Code/Source/Unhandled/Material/MaterialAssetTypeInfo.cpp b/Gems/LmbrCentral/Code/Source/Unhandled/Material/MaterialAssetTypeInfo.cpp index 907bd66cb8..4d5ec5e2f7 100644 --- a/Gems/LmbrCentral/Code/Source/Unhandled/Material/MaterialAssetTypeInfo.cpp +++ b/Gems/LmbrCentral/Code/Source/Unhandled/Material/MaterialAssetTypeInfo.cpp @@ -54,11 +54,6 @@ namespace LmbrCentral return "Icons/Components/Decal.svg"; } - AZ::Uuid MaterialAssetTypeInfo::GetComponentTypeId() const - { - return AZ::Uuid("{BA3890BD-D2E7-4DB6-95CD-7E7D5525567A}"); - } - // DccMaterialAssetTypeInfo DccMaterialAssetTypeInfo::~DccMaterialAssetTypeInfo() diff --git a/Gems/LmbrCentral/Code/Source/Unhandled/Material/MaterialAssetTypeInfo.h b/Gems/LmbrCentral/Code/Source/Unhandled/Material/MaterialAssetTypeInfo.h index b7f3294e89..7e9c9e5fb4 100644 --- a/Gems/LmbrCentral/Code/Source/Unhandled/Material/MaterialAssetTypeInfo.h +++ b/Gems/LmbrCentral/Code/Source/Unhandled/Material/MaterialAssetTypeInfo.h @@ -30,7 +30,6 @@ namespace LmbrCentral const char* GetAssetTypeDisplayName() const override; const char* GetGroup() const override; const char* GetBrowserIcon() const override; - AZ::Uuid GetComponentTypeId() const override; ////////////////////////////////////////////////////////////////////////////////////////////// void Register(); diff --git a/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken.fbx.assetinfo b/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken.fbx.assetinfo index 0036fa39f9..37480c52c1 100644 --- a/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken.fbx.assetinfo +++ b/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken.fbx.assetinfo @@ -23,42 +23,15 @@ { "Visible": true, "Position": [ - -0.08505599945783615, - 0.0, - 0.009370899759232998 - ], - "Rotation": [ - 0.7071437239646912, - 0.0, - 0.0, - 0.708984375 - ], - "propertyVisibilityFlags": 248 - }, - { - "$type": "CapsuleShapeConfiguration", - "Height": 0.191273495554924, - "Radius": 0.05063670128583908 - } - ] - ] - }, - { - "name": "def_c_neck_joint", - "shapes": [ - [ - { - "Visible": true, - "Position": [ - 0.08189810067415238, - -2.4586914726398847e-9, - -0.4713243842124939 + -0.03709467500448227, + -3.725290298461914e-9, + 0.013427333906292916 ], "propertyVisibilityFlags": 248 }, { "$type": "SphereShapeConfiguration", - "Radius": 0.2406993955373764 + "Radius": 0.12945009768009187 } ] ] @@ -70,42 +43,22 @@ { "Visible": true, "Position": [ - -2.0000000233721949e-7, - 0.012646200135350228, - -0.24104370176792146 - ], - "propertyVisibilityFlags": 248 - }, - { - "$type": "SphereShapeConfiguration", - "Radius": 0.24875959753990174 - } - ] - ] - }, - { - "name": "def_c_feather2_joint", - "shapes": [ - [ - { - "Visible": true, - "Position": [ - 0.06151500344276428, - 0.1300000101327896, - 7.729977369308472e-8 + 0.0, + 0.09497000277042389, + -0.19093050062656403 ], "Rotation": [ 0.0, - 0.7071062922477722, - 0.0, - 0.7071072459220886 + 0.662880003452301, + 0.7487256526947022, + 0.0 ], "propertyVisibilityFlags": 248 }, { "$type": "CapsuleShapeConfiguration", - "Height": 0.5730299949645996, - "Radius": 0.06151498109102249 + "Height": 0.8597599267959595, + "Radius": 0.27968019247055056 } ] ] diff --git a/Gems/NvCloth/Assets/slices/Cloth/Chicken_Actor.slice b/Gems/NvCloth/Assets/slices/Cloth/Chicken_Actor.slice index 6f64caf5f2..e7de286c1b 100644 --- a/Gems/NvCloth/Assets/slices/Cloth/Chicken_Actor.slice +++ b/Gems/NvCloth/Assets/slices/Cloth/Chicken_Actor.slice @@ -154,7 +154,7 @@ - + @@ -184,15 +184,15 @@ - + - + - + @@ -265,66 +265,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -511,6 +452,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gems/PhysX/Code/Include/PhysX/UserDataTypes.h b/Gems/PhysX/Code/Include/PhysX/UserDataTypes.h index 606bdea10c..8b6b5fa0d4 100644 --- a/Gems/PhysX/Code/Include/PhysX/UserDataTypes.h +++ b/Gems/PhysX/Code/Include/PhysX/UserDataTypes.h @@ -88,7 +88,7 @@ namespace PhysX Physics::RagdollNode* GetRagdollNode() const; void SetRagdollNode(Physics::RagdollNode* ragdollNode); - AzPhysics::SimulatedBody* GetWorldBody() const; + AzPhysics::SimulatedBody* GetSimulatedBody() const; private: diff --git a/Gems/PhysX/Code/Include/PhysX/UserDataTypes.inl b/Gems/PhysX/Code/Include/PhysX/UserDataTypes.inl index a24fdb27b2..d6897f9484 100644 --- a/Gems/PhysX/Code/Include/PhysX/UserDataTypes.inl +++ b/Gems/PhysX/Code/Include/PhysX/UserDataTypes.inl @@ -77,7 +77,7 @@ namespace PhysX inline AzPhysics::SimulatedBodyHandle ActorData::GetBodyHandle() const { - AzPhysics::SimulatedBody* body = GetWorldBody(); + AzPhysics::SimulatedBody* body = GetSimulatedBody(); if (body) { return body->m_bodyHandle; @@ -125,7 +125,7 @@ namespace PhysX m_payload.m_ragdollNode = ragdollNode; } - inline AzPhysics::SimulatedBody* ActorData::GetWorldBody() const + inline AzPhysics::SimulatedBody* ActorData::GetSimulatedBody() const { if (m_payload.m_rigidBody) { diff --git a/Gems/PhysX/Code/Source/BaseColliderComponent.h b/Gems/PhysX/Code/Source/BaseColliderComponent.h index cf4f43fb03..b23e6f1f09 100644 --- a/Gems/PhysX/Code/Source/BaseColliderComponent.h +++ b/Gems/PhysX/Code/Source/BaseColliderComponent.h @@ -100,10 +100,9 @@ namespace PhysX required.push_back(AZ_CRC("TransformService", 0x8ee22c50)); } - static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + static void GetIncompatibleServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& incompatible) { - // Not compatible with cry engine colliders - incompatible.push_back(AZ_CRC("ColliderService", 0x902d4e93)); + } // AZ::Component diff --git a/Gems/PhysX/Code/Source/Common/PhysXSceneQueryHelpers.cpp b/Gems/PhysX/Code/Source/Common/PhysXSceneQueryHelpers.cpp index 9903a311af..4b0a8f7329 100644 --- a/Gems/PhysX/Code/Source/Common/PhysXSceneQueryHelpers.cpp +++ b/Gems/PhysX/Code/Source/Common/PhysXSceneQueryHelpers.cpp @@ -258,9 +258,9 @@ namespace PhysX { ActorData* userData = Utils::GetUserData(actor); Physics::Shape* shape = Utils::GetUserData(pxShape); - if (userData != nullptr && userData->GetWorldBody()) + if (userData != nullptr && userData->GetSimulatedBody()) { - return GetPxHitType(m_filterCallback(userData->GetWorldBody(), shape)); + return GetPxHitType(m_filterCallback(userData->GetSimulatedBody(), shape)); } } return m_hitType; diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index 2dc8fc97ec..2785ab19e1 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp @@ -387,7 +387,7 @@ namespace PhysX void EditorColliderComponent::Deactivate() { - Physics::WorldBodyRequestBus::Handler::BusDisconnect(); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusDisconnect(); m_colliderDebugDraw.Disconnect(); AZ::Data::AssetBus::MultiHandler::BusDisconnect(); m_nonUniformScaleChangedHandler.Disconnect(); @@ -642,7 +642,7 @@ namespace PhysX m_colliderDebugDraw.ClearCachedGeometry(); - Physics::WorldBodyRequestBus::Handler::BusConnect(GetEntityId()); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(GetEntityId()); } AZ::Data::Asset EditorColliderComponent::GetMeshAsset() const @@ -1072,7 +1072,7 @@ namespace PhysX return AZ::Aabb::CreateNull(); } - AzPhysics::SimulatedBody* EditorColliderComponent::GetWorldBody() + AzPhysics::SimulatedBody* EditorColliderComponent::GetSimulatedBody() { if (m_sceneInterface && m_editorBodyHandle != AzPhysics::InvalidSimulatedBodyHandle) { @@ -1084,6 +1084,11 @@ namespace PhysX return nullptr; } + AzPhysics::SimulatedBodyHandle EditorColliderComponent::GetSimulatedBodyHandle() const + { + return m_editorBodyHandle; + } + AzPhysics::SceneQueryHit EditorColliderComponent::RayCast(const AzPhysics::RayCastRequest& request) { if (m_sceneInterface && m_editorBodyHandle != AzPhysics::InvalidSimulatedBodyHandle) diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.h b/Gems/PhysX/Code/Source/EditorColliderComponent.h index e78be1b959..1f9c00d4f5 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.h @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include @@ -105,7 +105,7 @@ namespace PhysX , private PhysX::ColliderShapeRequestBus::Handler , private AZ::Render::MeshComponentNotificationBus::Handler , private PhysX::EditorColliderComponentRequestBus::Handler - , private Physics::WorldBodyRequestBus::Handler + , private AzPhysics::SimulatedBodyComponentRequestsBus::Handler { public: AZ_RTTI(EditorColliderComponent, "{FD429282-A075-4966-857F-D0BBF186CFE6}", AzToolsFramework::Components::EditorComponentBase); @@ -205,12 +205,13 @@ namespace PhysX AZ::u32 OnConfigurationChanged(); void UpdateShapeConfigurationScale(); - // WorldBodyRequestBus + // AzPhysics::SimulatedBodyComponentRequestsBus::Handler overrides ... void EnablePhysics() override; void DisablePhysics() override; bool IsPhysicsEnabled() const override; AZ::Aabb GetAabb() const override; - AzPhysics::SimulatedBody* GetWorldBody() override; + AzPhysics::SimulatedBody* GetSimulatedBody() override; + AzPhysics::SimulatedBodyHandle GetSimulatedBodyHandle() const override; AzPhysics::SceneQueryHit RayCast(const AzPhysics::RayCastRequest& request) override; // Mesh collider diff --git a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp index fb663dd9a4..b6517b0499 100644 --- a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp @@ -265,14 +265,14 @@ namespace PhysX } CreateEditorWorldRigidBody(); - Physics::WorldBodyRequestBus::Handler::BusConnect(GetEntityId()); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(GetEntityId()); } void EditorRigidBodyComponent::Deactivate() { m_debugDisplayDataChangeHandler.Disconnect(); - Physics::WorldBodyRequestBus::Handler::BusDisconnect(); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusDisconnect(); m_nonUniformScaleChangedHandler.Disconnect(); m_sceneStartSimHandler.Disconnect(); Physics::ColliderComponentEventBus::Handler::BusDisconnect(); @@ -461,11 +461,16 @@ namespace PhysX return AZ::Aabb::CreateNull(); } - AzPhysics::SimulatedBody* EditorRigidBodyComponent::GetWorldBody() + AzPhysics::SimulatedBody* EditorRigidBodyComponent::GetSimulatedBody() { return m_editorBody; } + AzPhysics::SimulatedBodyHandle EditorRigidBodyComponent::GetSimulatedBodyHandle() const + { + return m_rigidBodyHandle; + } + AzPhysics::SceneQueryHit EditorRigidBodyComponent::RayCast(const AzPhysics::RayCastRequest& request) { if (m_editorBody) diff --git a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.h b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.h index 2d42812292..b2b199e6be 100644 --- a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.h +++ b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.h @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include @@ -49,7 +49,7 @@ namespace PhysX , protected AzFramework::EntityDebugDisplayEventBus::Handler , private AZ::TransformNotificationBus::Handler , private Physics::ColliderComponentEventBus::Handler - , private Physics::WorldBodyRequestBus::Handler + , private AzPhysics::SimulatedBodyComponentRequestsBus::Handler { public: AZ_EDITOR_COMPONENT(EditorRigidBodyComponent, "{F2478E6B-001A-4006-9D7E-DCB5A6B041DD}", AzToolsFramework::Components::EditorComponentBase); @@ -107,12 +107,13 @@ namespace PhysX // Physics::ColliderComponentEventBus void OnColliderChanged() override; - // WorldBodyRequestBus + // AzPhysics::SimulatedBodyComponentRequestsBus::Handler overrides ... void EnablePhysics() override; void DisablePhysics() override; bool IsPhysicsEnabled() const override; AZ::Aabb GetAabb() const override; - AzPhysics::SimulatedBody* GetWorldBody() override; + AzPhysics::SimulatedBody* GetSimulatedBody() override; + AzPhysics::SimulatedBodyHandle GetSimulatedBodyHandle() const override; AzPhysics::SceneQueryHit RayCast(const AzPhysics::RayCastRequest& request) override; void CreateEditorWorldRigidBody(); diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index aa1c945fa3..0a8bca33ea 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -120,8 +120,6 @@ namespace PhysX void EditorShapeColliderComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { - // Not compatible with Legacy Cry Physics services - incompatible.push_back(AZ_CRC("ColliderService", 0x902d4e93)); incompatible.push_back(AZ_CRC("LegacyCryPhysicsService", 0xbb370351)); incompatible.push_back(AZ_CRC("PhysXShapeColliderService", 0x98a7e779)); } @@ -273,7 +271,7 @@ namespace PhysX m_editorBody = azdynamic_cast(m_sceneInterface->GetSimulatedBodyFromHandle(m_editorSceneHandle, m_editorBodyHandle)); } - Physics::WorldBodyRequestBus::Handler::BusConnect(GetEntityId()); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(GetEntityId()); } AZ::u32 EditorShapeColliderComponent::OnConfigurationChanged() @@ -663,7 +661,7 @@ namespace PhysX void EditorShapeColliderComponent::Deactivate() { - Physics::WorldBodyRequestBus::Handler::BusDisconnect(); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusDisconnect(); m_colliderDebugDraw.Disconnect(); m_nonUniformScaleChangedHandler.Disconnect(); @@ -761,11 +759,16 @@ namespace PhysX return AZ::Aabb::CreateNull(); } - AzPhysics::SimulatedBody* EditorShapeColliderComponent::GetWorldBody() + AzPhysics::SimulatedBody* EditorShapeColliderComponent::GetSimulatedBody() { return m_editorBody; } + AzPhysics::SimulatedBodyHandle EditorShapeColliderComponent::GetSimulatedBodyHandle() const + { + return m_editorBodyHandle; + } + AzPhysics::SceneQueryHit EditorShapeColliderComponent::RayCast(const AzPhysics::RayCastRequest& request) { if (m_editorBody) diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h index bbb14fe6a4..bcf5ac4eba 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -68,7 +68,7 @@ namespace PhysX , protected DebugDraw::DisplayCallback , protected LmbrCentral::ShapeComponentNotificationsBus::Handler , private PhysX::ColliderShapeRequestBus::Handler - , protected Physics::WorldBodyRequestBus::Handler + , protected AzPhysics::SimulatedBodyComponentRequestsBus::Handler { public: AZ_EDITOR_COMPONENT(EditorShapeColliderComponent, "{2389DDC7-871B-42C6-9C95-2A679DDA0158}", @@ -120,12 +120,13 @@ namespace PhysX // handling for non-uniform scale void OnNonUniformScaleChanged(const AZ::Vector3& scale); - // WorldBodyRequestBus + // AzPhysics::SimulatedBodyComponentRequestsBus::Handler overrides ... void EnablePhysics() override; void DisablePhysics() override; bool IsPhysicsEnabled() const override; AZ::Aabb GetAabb() const override; - AzPhysics::SimulatedBody* GetWorldBody() override; + AzPhysics::SimulatedBody* GetSimulatedBody() override; + AzPhysics::SimulatedBodyHandle GetSimulatedBodyHandle() const override; AzPhysics::SceneQueryHit RayCast(const AzPhysics::RayCastRequest& request) override; // LmbrCentral::ShapeComponentNotificationBus diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp index ca02554036..473e9534cb 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp @@ -87,7 +87,7 @@ namespace PhysX AZ::TransformNotificationBus::Handler::BusConnect(GetEntityId()); Physics::CharacterRequestBus::Handler::BusConnect(GetEntityId()); Physics::CollisionFilteringRequestBus::Handler::BusConnect(GetEntityId()); - Physics::WorldBodyRequestBus::Handler::BusConnect(GetEntityId()); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(GetEntityId()); } void CharacterControllerComponent::Deactivate() @@ -95,7 +95,7 @@ namespace PhysX DestroyController(); Physics::CollisionFilteringRequestBus::Handler::BusDisconnect(); - Physics::WorldBodyRequestBus::Handler::BusDisconnect(); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::Handler::BusDisconnect(); Physics::CharacterRequestBus::Handler::BusDisconnect(); } @@ -215,11 +215,20 @@ namespace PhysX return AZ::Aabb::CreateNull(); } - AzPhysics::SimulatedBody* CharacterControllerComponent::GetWorldBody() + AzPhysics::SimulatedBody* CharacterControllerComponent::GetSimulatedBody() { return GetCharacter(); } + AzPhysics::SimulatedBodyHandle CharacterControllerComponent::GetSimulatedBodyHandle() const + { + if (m_controller) + { + return m_controller->m_bodyHandle; + } + return AzPhysics::InvalidSimulatedBodyHandle; + } + AzPhysics::SceneQueryHit CharacterControllerComponent::RayCast(const AzPhysics::RayCastRequest& request) { if (m_controller) @@ -456,7 +465,5 @@ namespace PhysX m_preSimulateHandler.Disconnect(); CharacterControllerRequestBus::Handler::BusDisconnect(); - - Physics::WorldBodyNotificationBus::Event(GetEntityId(), &Physics::WorldBodyNotifications::OnPhysicsDisabled); } } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h index ca956b1757..31f5051ac4 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include @@ -35,7 +35,7 @@ namespace PhysX class CharacterControllerComponent : public AZ::Component , public Physics::CharacterRequestBus::Handler - , public Physics::WorldBodyRequestBus::Handler + , public AzPhysics::SimulatedBodyComponentRequestsBus::Handler , public AZ::TransformNotificationBus::Handler , public CharacterControllerRequestBus::Handler , public Physics::CollisionFilteringRequestBus::Handler @@ -99,12 +99,13 @@ namespace PhysX bool IsPresent() const override { return IsPhysicsEnabled(); } Physics::Character* GetCharacter() override; - // WorldBodyRequestBus + // AzPhysics::SimulatedBodyComponentRequestsBus::Handler overrides ... void EnablePhysics() override; void DisablePhysics() override; bool IsPhysicsEnabled() const override; AZ::Aabb GetAabb() const override; - AzPhysics::SimulatedBody* GetWorldBody() override; + AzPhysics::SimulatedBody* GetSimulatedBody() override; + AzPhysics::SimulatedBodyHandle GetSimulatedBodyHandle() const override; AzPhysics::SceneQueryHit RayCast(const AzPhysics::RayCastRequest& request) override; // CharacterControllerRequestBus diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp index e0c3ba40ba..70373f8db2 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterGameplayComponent.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -158,7 +157,7 @@ namespace PhysX void CharacterGameplayComponent::Activate() { AzPhysics::SimulatedBody* worldBody = nullptr; - Physics::WorldBodyRequestBus::EventResult(worldBody, GetEntityId(), &Physics::WorldBodyRequests::GetWorldBody); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(worldBody, GetEntityId(), &AzPhysics::SimulatedBodyComponentRequests::GetSimulatedBody); if (worldBody) { if (auto* sceneInterface = AZ::Interface::Get()) diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp index 85060445fc..4c58187fb8 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.cpp @@ -257,11 +257,20 @@ namespace PhysX return AZ::Aabb::CreateNull(); } - AzPhysics::SimulatedBody* RagdollComponent::GetWorldBody() + AzPhysics::SimulatedBody* RagdollComponent::GetSimulatedBody() { return GetRagdoll(); } + AzPhysics::SimulatedBodyHandle RagdollComponent::GetSimulatedBodyHandle() const + { + if (m_ragdoll) + { + return m_ragdoll->m_bodyHandle; + } + return AzPhysics::InvalidSimulatedBodyHandle; + } + AzPhysics::SceneQueryHit RagdollComponent::RayCast(const AzPhysics::RayCastRequest& request) { if (m_ragdoll) @@ -357,7 +366,7 @@ namespace PhysX } AzFramework::RagdollPhysicsRequestBus::Handler::BusConnect(GetEntityId()); - Physics::WorldBodyRequestBus::Handler::BusConnect(GetEntityId()); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(GetEntityId()); AzFramework::RagdollPhysicsNotificationBus::Event(GetEntityId(), &AzFramework::RagdollPhysicsNotifications::OnRagdollActivated); @@ -367,7 +376,6 @@ namespace PhysX { if (m_ragdoll) { - Physics::WorldBodyRequestBus::Handler::BusDisconnect(); AzFramework::RagdollPhysicsRequestBus::Handler::BusDisconnect(); AzFramework::RagdollPhysicsNotificationBus::Event(GetEntityId(), &AzFramework::RagdollPhysicsNotifications::OnRagdollDeactivated); diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.h b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.h index 1b616dda79..e7397af877 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.h +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/RagdollComponent.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include namespace AzPhysics { @@ -28,7 +28,7 @@ namespace PhysX class RagdollComponent : public AZ::Component , public AzFramework::RagdollPhysicsRequestBus::Handler - , public Physics::WorldBodyRequestBus::Handler + , public AzPhysics::SimulatedBodyComponentRequestsBus::Handler , public AzFramework::CharacterPhysicsDataNotificationBus::Handler { public: @@ -81,12 +81,13 @@ namespace PhysX void SetNodeState(size_t nodeIndex, const Physics::RagdollNodeState& nodeState) override; Physics::RagdollNode* GetNode(size_t nodeIndex) const override; - // WorldBodyRequestBus + // AzPhysics::SimulatedBodyComponentRequestsBus::Handler overrides ... void EnablePhysics() override; void DisablePhysics() override; bool IsPhysicsEnabled() const override; AZ::Aabb GetAabb() const override; - AzPhysics::SimulatedBody* GetWorldBody() override; + AzPhysics::SimulatedBody* GetSimulatedBody() override; + AzPhysics::SimulatedBodyHandle GetSimulatedBodyHandle() const override; AzPhysics::SceneQueryHit RayCast(const AzPhysics::RayCastRequest& request) override; // CharacterPhysicsDataNotificationBus diff --git a/Gems/PhysX/Code/Source/RigidBodyComponent.cpp b/Gems/PhysX/Code/Source/RigidBodyComponent.cpp index 8984343fe5..cf2e306cc7 100644 --- a/Gems/PhysX/Code/Source/RigidBodyComponent.cpp +++ b/Gems/PhysX/Code/Source/RigidBodyComponent.cpp @@ -189,7 +189,7 @@ namespace PhysX } Physics::RigidBodyRequestBus::Handler::BusDisconnect(); - Physics::WorldBodyRequestBus::Handler::BusDisconnect(); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::MultiHandler::BusDisconnect(); m_sceneFinishSimHandler.Disconnect(); AZ::TickBus::Handler::BusDisconnect(); @@ -309,7 +309,7 @@ namespace PhysX AZ::TickBus::Handler::BusConnect(); AZ::TransformNotificationBus::MultiHandler::BusConnect(GetEntityId()); Physics::RigidBodyRequestBus::Handler::BusConnect(GetEntityId()); - Physics::WorldBodyRequestBus::Handler::BusConnect(GetEntityId()); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(GetEntityId()); } void RigidBodyComponent::EnablePhysics() @@ -341,7 +341,6 @@ namespace PhysX m_initialScale = transform.ExtractScale(); Physics::RigidBodyNotificationBus::Event(GetEntityId(), &Physics::RigidBodyNotificationBus::Events::OnPhysicsEnabled); - Physics::WorldBodyNotificationBus::Event(GetEntityId(), &Physics::WorldBodyNotifications::OnPhysicsEnabled); } void RigidBodyComponent::DisablePhysics() @@ -352,7 +351,6 @@ namespace PhysX } Physics::RigidBodyNotificationBus::Event(GetEntityId(), &Physics::RigidBodyNotificationBus::Events::OnPhysicsDisabled); - Physics::WorldBodyNotificationBus::Event(GetEntityId(), &Physics::WorldBodyNotifications::OnPhysicsDisabled); } bool RigidBodyComponent::IsPhysicsEnabled() const @@ -526,11 +524,16 @@ namespace PhysX return m_rigidBody; } - AzPhysics::SimulatedBody* RigidBodyComponent::GetWorldBody() + AzPhysics::SimulatedBody* RigidBodyComponent::GetSimulatedBody() { return m_rigidBody; } + AzPhysics::SimulatedBodyHandle RigidBodyComponent::GetSimulatedBodyHandle() const + { + return m_rigidBodyHandle; + } + AzPhysics::SceneQueryHit RigidBodyComponent::RayCast(const AzPhysics::RayCastRequest& request) { if (m_rigidBody) diff --git a/Gems/PhysX/Code/Source/RigidBodyComponent.h b/Gems/PhysX/Code/Source/RigidBodyComponent.h index c46e136669..12c4d3f5ff 100644 --- a/Gems/PhysX/Code/Source/RigidBodyComponent.h +++ b/Gems/PhysX/Code/Source/RigidBodyComponent.h @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -35,7 +35,7 @@ namespace PhysX class RigidBodyComponent : public AZ::Component , public Physics::RigidBodyRequestBus::Handler - , public Physics::WorldBodyRequestBus::Handler + , public AzPhysics::SimulatedBodyComponentRequestsBus::Handler , public AZ::TickBus::Handler , public AzFramework::SliceGameEntityOwnershipServiceNotificationBus::Handler , protected AZ::TransformNotificationBus::MultiHandler @@ -120,8 +120,9 @@ namespace PhysX void SetSleepThreshold(float threshold) override; AzPhysics::RigidBody* GetRigidBody() override; - // WorldBodyRequestBus - AzPhysics::SimulatedBody* GetWorldBody() override; + // AzPhysics::SimulatedBodyComponentRequestsBus::Handler overrides ... + AzPhysics::SimulatedBody* GetSimulatedBody() override; + AzPhysics::SimulatedBodyHandle GetSimulatedBodyHandle() const override; // SliceGameEntityOwnershipServiceNotificationBus void OnSliceInstantiated(const AZ::Data::AssetId&, const AZ::SliceComponent::SliceInstanceAddress&, diff --git a/Gems/PhysX/Code/Source/Scene/PhysXSceneSimulationEventCallback.cpp b/Gems/PhysX/Code/Source/Scene/PhysXSceneSimulationEventCallback.cpp index fbe7b36a03..c19e047062 100644 --- a/Gems/PhysX/Code/Source/Scene/PhysXSceneSimulationEventCallback.cpp +++ b/Gems/PhysX/Code/Source/Scene/PhysXSceneSimulationEventCallback.cpp @@ -83,8 +83,8 @@ namespace PhysX continue; } - AzPhysics::SimulatedBody* body1 = actorData1->GetWorldBody(); - AzPhysics::SimulatedBody* body2 = actorData2->GetWorldBody(); + AzPhysics::SimulatedBody* body1 = actorData1->GetSimulatedBody(); + AzPhysics::SimulatedBody* body2 = actorData2->GetSimulatedBody(); if (!body1 || !body2) { @@ -161,7 +161,7 @@ namespace PhysX } ActorData* triggerBodyActorData = Utils::GetUserData(triggerPair.triggerActor); - AzPhysics::SimulatedBody* triggerBody = triggerBodyActorData->GetWorldBody(); + AzPhysics::SimulatedBody* triggerBody = triggerBodyActorData->GetSimulatedBody(); if (!triggerBody) { AZ_Error("PhysX", false, "onTrigger:: trigger body was invalid"); @@ -174,7 +174,7 @@ namespace PhysX } ActorData* otherActorData = Utils::GetUserData(triggerPair.otherActor); - AzPhysics::SimulatedBody* otherBody = otherActorData->GetWorldBody(); + AzPhysics::SimulatedBody* otherBody = otherActorData->GetSimulatedBody(); if (!otherBody) { AZ_Error("PhysX", false, "onTrigger:: otherBody was invalid"); diff --git a/Gems/PhysX/Code/Source/StaticRigidBodyComponent.cpp b/Gems/PhysX/Code/Source/StaticRigidBodyComponent.cpp index 9387884999..79e5ea8204 100644 --- a/Gems/PhysX/Code/Source/StaticRigidBodyComponent.cpp +++ b/Gems/PhysX/Code/Source/StaticRigidBodyComponent.cpp @@ -62,8 +62,6 @@ namespace PhysX void StaticRigidBodyComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { - // Not compatible with cry engine colliders - incompatible.push_back(AZ_CRC("ColliderService", 0x902d4e93)); // There can be only one StaticRigidBodyComponent per entity incompatible.push_back(AZ_CRC("PhysXStaticRigidBodyService", 0xaae8973b)); // Cannot have both StaticRigidBodyComponent and RigidBodyComponent @@ -75,11 +73,6 @@ namespace PhysX dependent.push_back(AZ_CRC("PhysXColliderService", 0x4ff43f7c)); } - PhysX::StaticRigidBody* StaticRigidBodyComponent::GetStaticRigidBody() - { - return m_staticRigidBody; - } - void StaticRigidBodyComponent::InitStaticRigidBody() { AZ::Transform transform = AZ::Transform::CreateIdentity(); @@ -117,8 +110,7 @@ namespace PhysX InitStaticRigidBody(); - Physics::WorldBodyRequestBus::Handler::BusConnect(GetEntityId()); - Physics::WorldBodyNotificationBus::Event(GetEntityId(), &Physics::WorldBodyNotifications::OnPhysicsEnabled); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(GetEntityId()); } void StaticRigidBodyComponent::Deactivate() @@ -130,7 +122,7 @@ namespace PhysX m_staticRigidBody = nullptr; } - Physics::WorldBodyRequestBus::Handler::BusDisconnect(); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::Handler::BusDisconnect(); } @@ -149,8 +141,6 @@ namespace PhysX { sceneInterface->EnableSimulationOfBody(m_attachedSceneHandle, m_staticRigidBodyHandle); } - - Physics::WorldBodyNotificationBus::Event(GetEntityId(), &Physics::WorldBodyNotifications::OnPhysicsEnabled); } void StaticRigidBodyComponent::DisablePhysics() @@ -159,8 +149,6 @@ namespace PhysX { sceneInterface->DisableSimulationOfBody(m_attachedSceneHandle, m_staticRigidBodyHandle); } - - Physics::WorldBodyNotificationBus::Event(GetEntityId(), &Physics::WorldBodyNotifications::OnPhysicsDisabled); } bool StaticRigidBodyComponent::IsPhysicsEnabled() const @@ -173,7 +161,12 @@ namespace PhysX return m_staticRigidBody->GetAabb(); } - AzPhysics::SimulatedBody* StaticRigidBodyComponent::GetWorldBody() + AzPhysics::SimulatedBodyHandle StaticRigidBodyComponent::GetSimulatedBodyHandle() const + { + return m_staticRigidBodyHandle; + } + + AzPhysics::SimulatedBody* StaticRigidBodyComponent::GetSimulatedBody() { return m_staticRigidBody; } diff --git a/Gems/PhysX/Code/Source/StaticRigidBodyComponent.h b/Gems/PhysX/Code/Source/StaticRigidBodyComponent.h index f846329ca0..660521ab7a 100644 --- a/Gems/PhysX/Code/Source/StaticRigidBodyComponent.h +++ b/Gems/PhysX/Code/Source/StaticRigidBodyComponent.h @@ -13,7 +13,7 @@ #include #include -#include +#include #include namespace AzPhysics @@ -27,7 +27,7 @@ namespace PhysX class StaticRigidBodyComponent final : public AZ::Component - , public Physics::WorldBodyRequestBus::Handler + , public AzPhysics::SimulatedBodyComponentRequestsBus::Handler , private AZ::TransformNotificationBus::Handler { public: @@ -44,14 +44,14 @@ namespace PhysX static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); - PhysX::StaticRigidBody* GetStaticRigidBody(); - - // WorldBodyRequestBus + // AzPhysics::SimulatedBodyComponentRequestsBus::Handler overrides ... void EnablePhysics() override; void DisablePhysics() override; bool IsPhysicsEnabled() const override; AZ::Aabb GetAabb() const override; - AzPhysics::SimulatedBody* GetWorldBody() override; + AzPhysics::SimulatedBodyHandle GetSimulatedBodyHandle() const override; + AzPhysics::SimulatedBody* GetSimulatedBody() override; + AzPhysics::SceneQueryHit RayCast(const AzPhysics::RayCastRequest& request) override; private: diff --git a/Gems/PhysX/Code/Tests/CharacterControllerTests.cpp b/Gems/PhysX/Code/Tests/CharacterControllerTests.cpp index 218571c01c..4e111787e6 100644 --- a/Gems/PhysX/Code/Tests/CharacterControllerTests.cpp +++ b/Gems/PhysX/Code/Tests/CharacterControllerTests.cpp @@ -460,14 +460,14 @@ namespace PhysX characterEntity->Activate(); bool physicsEnabled = false; - Physics::WorldBodyRequestBus::EventResult(physicsEnabled, characterEntity->GetId(), - &Physics::WorldBodyRequestBus::Events::IsPhysicsEnabled); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(physicsEnabled, characterEntity->GetId(), + &AzPhysics::SimulatedBodyComponentRequestsBus::Events::IsPhysicsEnabled); EXPECT_TRUE(physicsEnabled); // when physics is disabled - Physics::WorldBodyRequestBus::Event(characterEntity->GetId(), &Physics::WorldBodyRequestBus::Events::DisablePhysics); - Physics::WorldBodyRequestBus::EventResult(physicsEnabled, characterEntity->GetId(), - &Physics::WorldBodyRequestBus::Events::IsPhysicsEnabled); + AzPhysics::SimulatedBodyComponentRequestsBus::Event(characterEntity->GetId(), &AzPhysics::SimulatedBodyComponentRequestsBus::Events::DisablePhysics); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(physicsEnabled, characterEntity->GetId(), + &AzPhysics::SimulatedBodyComponentRequestsBus::Events::IsPhysicsEnabled); EXPECT_FALSE(physicsEnabled); // expect no error occurs when sending common events diff --git a/Gems/PhysX/Code/Tests/ColliderScalingTests.cpp b/Gems/PhysX/Code/Tests/ColliderScalingTests.cpp index 51a11c7605..ae65fcfc23 100644 --- a/Gems/PhysX/Code/Tests/ColliderScalingTests.cpp +++ b/Gems/PhysX/Code/Tests/ColliderScalingTests.cpp @@ -77,7 +77,7 @@ namespace PhysXEditorTests EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); // since there was no editor rigid body component, the runtime entity should have a static rigid body - const auto* staticBody = gameEntity->FindComponent()->GetStaticRigidBody(); + const auto* staticBody = azdynamic_cast(gameEntity->FindComponent()->GetSimulatedBody()); const AZ::Aabb aabb = staticBody->GetAabb(); EXPECT_THAT(aabb.GetMin(), UnitTest::IsCloseTolerance(AZ::Vector3(5.6045f, 4.9960f, 11.7074f), 1e-3f)); @@ -153,7 +153,7 @@ namespace PhysXEditorTests EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); // since there was no editor rigid body component, the runtime entity should have a static rigid body - const auto* staticBody = gameEntity->FindComponent()->GetStaticRigidBody(); + const auto* staticBody = azdynamic_cast(gameEntity->FindComponent()->GetSimulatedBody()); const AZ::Aabb aabb = staticBody->GetAabb(); @@ -231,7 +231,7 @@ namespace PhysXEditorTests EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); // since there was no editor rigid body component, the runtime entity should have a static rigid body - const auto* staticBody = gameEntity->FindComponent()->GetStaticRigidBody(); + const auto* staticBody = azdynamic_cast(gameEntity->FindComponent()->GetSimulatedBody()); const AZ::Aabb aabb = staticBody->GetAabb(); diff --git a/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp b/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp index 94d86e27c7..043bd60acd 100644 --- a/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp +++ b/Gems/PhysX/Code/Tests/EditorTestUtilities.cpp @@ -121,7 +121,7 @@ namespace PhysXEditorTests EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); // since there was no editor rigid body component, the runtime entity should have a static rigid body - const auto* staticBody = gameEntity->FindComponent()->GetStaticRigidBody(); + const auto* staticBody = azdynamic_cast(gameEntity->FindComponent()->GetSimulatedBody()); const auto* pxRigidStatic = static_cast(staticBody->GetNativePointer()); PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene()); diff --git a/Gems/PhysX/Code/Tests/PhysXComponentBusTests.cpp b/Gems/PhysX/Code/Tests/PhysXComponentBusTests.cpp index 0422746f80..d9d6d47b94 100644 --- a/Gems/PhysX/Code/Tests/PhysXComponentBusTests.cpp +++ b/Gems/PhysX/Code/Tests/PhysXComponentBusTests.cpp @@ -613,17 +613,17 @@ namespace PhysX // Create 3 colliders, one of each type and check that the AABB of their body is the expected EntityPtr box = TestUtils::CreateBoxEntity(m_testSceneHandle, AZ::Vector3(0, 0, 0), AZ::Vector3(32, 32, 32)); AZ::Aabb boxAABB; - Physics::WorldBodyRequestBus::EventResult(boxAABB, box->GetId(), &Physics::WorldBodyRequests::GetAabb); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(boxAABB, box->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetAabb); EXPECT_TRUE(boxAABB.GetMin().IsClose(AZ::Vector3(-16, -16, -16)) && boxAABB.GetMax().IsClose(AZ::Vector3(16, 16, 16))); EntityPtr sphere = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(-100, 0, 0), 16); AZ::Aabb sphereAABB; - Physics::WorldBodyRequestBus::EventResult(sphereAABB, sphere->GetId(), &Physics::WorldBodyRequests::GetAabb); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(sphereAABB, sphere->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetAabb); EXPECT_TRUE(sphereAABB.GetMin().IsClose(AZ::Vector3(-16 -100, -16, -16)) && sphereAABB.GetMax().IsClose(AZ::Vector3(16 -100, 16, 16))); EntityPtr capsule = TestUtils::CreateCapsuleEntity(m_testSceneHandle, AZ::Vector3(100, 0, 0), 128, 16); AZ::Aabb capsuleAABB; - Physics::WorldBodyRequestBus::EventResult(capsuleAABB, capsule->GetId(), &Physics::WorldBodyRequests::GetAabb); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(capsuleAABB, capsule->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetAabb); EXPECT_TRUE(capsuleAABB.GetMin().IsClose(AZ::Vector3(-16 +100, -16, -64)) && capsuleAABB.GetMax().IsClose(AZ::Vector3(16 +100, 16, 64))); } @@ -632,17 +632,17 @@ namespace PhysX // Create 3 colliders, one of each type and check that the AABB of their body is the expected EntityPtr box = TestUtils::CreateStaticBoxEntity(m_testSceneHandle, AZ::Vector3(0, 0, 0), AZ::Vector3(32, 32, 32)); AZ::Aabb boxAABB; - Physics::WorldBodyRequestBus::EventResult(boxAABB, box->GetId(), &Physics::WorldBodyRequests::GetAabb); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(boxAABB, box->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetAabb); EXPECT_TRUE(boxAABB.GetMin().IsClose(AZ::Vector3(-16, -16, -16)) && boxAABB.GetMax().IsClose(AZ::Vector3(16, 16, 16))); EntityPtr sphere = TestUtils::CreateStaticSphereEntity(m_testSceneHandle, AZ::Vector3(-100, 0, 0), 16); AZ::Aabb sphereAABB; - Physics::WorldBodyRequestBus::EventResult(sphereAABB, sphere->GetId(), &Physics::WorldBodyRequests::GetAabb); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(sphereAABB, sphere->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetAabb); EXPECT_TRUE(sphereAABB.GetMin().IsClose(AZ::Vector3(-16 -100, -16, -16)) && sphereAABB.GetMax().IsClose(AZ::Vector3(16 -100, 16, 16))); EntityPtr capsule = TestUtils::CreateStaticCapsuleEntity(m_testSceneHandle, AZ::Vector3(100, 0, 0), 128, 16); AZ::Aabb capsuleAABB; - Physics::WorldBodyRequestBus::EventResult(capsuleAABB, capsule->GetId(), &Physics::WorldBodyRequests::GetAabb); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(capsuleAABB, capsule->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetAabb); EXPECT_TRUE(capsuleAABB.GetMin().IsClose(AZ::Vector3(-16 +100, -16, -64)) && capsuleAABB.GetMax().IsClose(AZ::Vector3(16 +100, 16, 64))); } @@ -662,19 +662,19 @@ namespace PhysX request.m_direction = AZ::Vector3(0, 0, -1); request.m_distance = 200.f; - Physics::WorldBodyRequestBus::Event(entity->GetId(), &Physics::WorldBodyRequests::DisablePhysics); + AzPhysics::SimulatedBodyComponentRequestsBus::Event(entity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::DisablePhysics); bool enabled = true; - Physics::WorldBodyRequestBus::EventResult(enabled, entity->GetId(), &Physics::WorldBodyRequests::IsPhysicsEnabled); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(enabled, entity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::IsPhysicsEnabled); EXPECT_FALSE(enabled); AzPhysics::SceneQueryHits result = sceneInterface->QueryScene(sceneHandle, &request); EXPECT_FALSE(result); - Physics::WorldBodyRequestBus::Event(entity->GetId(), &Physics::WorldBodyRequests::EnablePhysics); + AzPhysics::SimulatedBodyComponentRequestsBus::Event(entity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::EnablePhysics); enabled = false; - Physics::WorldBodyRequestBus::EventResult(enabled, entity->GetId(), &Physics::WorldBodyRequests::IsPhysicsEnabled); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(enabled, entity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::IsPhysicsEnabled); EXPECT_TRUE(enabled); result = sceneInterface->QueryScene(sceneHandle, &request); @@ -718,7 +718,7 @@ namespace PhysX request.m_distance = 200.0f; AzPhysics::SceneQueryHit hit; - Physics::WorldBodyRequestBus::EventResult(hit, staticBoxEntity->GetId(), &Physics::WorldBodyRequests::RayCast, request); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(hit, staticBoxEntity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::RayCast, request); EXPECT_TRUE(hit); @@ -922,7 +922,7 @@ namespace PhysX static const RayCastFunc WorldBodyRaycastEBusCall = []([[maybe_unused]] AZ::EntityId entityId, [[maybe_unused]] const AzPhysics::RayCastRequest& request) { AzPhysics::SceneQueryHit ret; - Physics::WorldBodyRequestBus::EventResult(ret, entityId, &Physics::WorldBodyRequests::RayCast, request); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(ret, entityId, &AzPhysics::SimulatedBodyComponentRequests::RayCast, request); return ret; }; diff --git a/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp b/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp index e6b777f8c0..8e4da0fe4b 100644 --- a/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp +++ b/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp @@ -312,7 +312,7 @@ namespace PhysX { // set up a trigger box auto triggerBox = TestUtils::CreateTriggerAtPosition(AZ::Vector3(0.0f, 0.0f, 12.0f)); - auto triggerBody = triggerBox->FindComponent()->GetStaticRigidBody(); + auto* triggerBody = azdynamic_cast(triggerBox->FindComponent()->GetSimulatedBody()); auto triggerShape = triggerBody->GetShape(0); TestTriggerAreaNotificationListener testTriggerAreaNotificationListener(triggerBox->GetId()); @@ -445,7 +445,7 @@ namespace PhysX auto obj02 = TestUtils::AddStaticUnitTestObject(m_testSceneHandle, AZ::Vector3(0.0f, 0.0f, 0.0f), "TestBox01"); auto body01 = obj01->FindComponent()->GetRigidBody(); - auto body02 = obj02->FindComponent()->GetStaticRigidBody(); + auto* body02 = azdynamic_cast(obj02->FindComponent()->GetSimulatedBody()); auto shape01 = body01->GetShape(0).get(); auto shape02 = body02->GetShape(0).get(); @@ -588,7 +588,7 @@ namespace PhysX { // set up a trigger box auto triggerBox = TestUtils::CreateTriggerAtPosition(AZ::Vector3(0.0f, 0.0f, 0.0f)); - auto triggerBody = triggerBox->FindComponent()->GetStaticRigidBody(); + auto* triggerBody = azdynamic_cast(triggerBox->FindComponent()->GetSimulatedBody()); // Create a test box above the trigger so when it falls down it'd enter and leave the trigger box auto testBox = TestUtils::AddUnitTestObject(m_testSceneHandle, AZ::Vector3(0.0f, 0.0f, 1.5f), "TestBox"); @@ -628,7 +628,7 @@ namespace PhysX { // Set up a static non trigger box auto staticBox = TestUtils::AddStaticUnitTestObject(m_testSceneHandle, AZ::Vector3(0.0f, 0.0f, 0.0f)); - auto staticBody = staticBox->FindComponent()->GetStaticRigidBody(); + auto* staticBody = azdynamic_cast(staticBox->FindComponent()->GetSimulatedBody()); // Create a test trigger box above the static box so when it falls down it'd enter and leave the trigger box auto dynamicTrigger = TestUtils::CreateDynamicTriggerAtPosition(AZ::Vector3(0.0f, 0.0f, 5.0f)); diff --git a/Gems/PhysX/Code/Tests/RagdollTests.cpp b/Gems/PhysX/Code/Tests/RagdollTests.cpp index ec803c1707..477e754d74 100644 --- a/Gems/PhysX/Code/Tests/RagdollTests.cpp +++ b/Gems/PhysX/Code/Tests/RagdollTests.cpp @@ -37,7 +37,7 @@ namespace PhysX - + )DELIMITER"; diff --git a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp index 422385a777..b00e226078 100644 --- a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp @@ -138,7 +138,7 @@ namespace PhysXEditorTests EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); // since there was no editor rigid body component, the runtime entity should have a static rigid body - const auto* staticBody = gameEntity->FindComponent()->GetStaticRigidBody(); + const auto* staticBody = azdynamic_cast(gameEntity->FindComponent()->GetSimulatedBody()); const auto* pxRigidStatic = static_cast(staticBody->GetNativePointer()); PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene()); @@ -196,7 +196,7 @@ namespace PhysXEditorTests EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); // since there was no editor rigid body component, the runtime entity should have a static rigid body - const auto* staticBody = gameEntity->FindComponent()->GetStaticRigidBody(); + const auto* staticBody = azdynamic_cast(gameEntity->FindComponent()->GetSimulatedBody()); const auto* pxRigidStatic = static_cast(staticBody->GetNativePointer()); PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene()); @@ -247,7 +247,7 @@ namespace PhysXEditorTests EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); // since there was no editor rigid body component, the runtime entity should have a static rigid body - const auto* staticBody = gameEntity->FindComponent()->GetStaticRigidBody(); + const auto* staticBody = azdynamic_cast(gameEntity->FindComponent()->GetSimulatedBody()); // the vertices of the input polygon prism ranged from (0, 0) to (3, 3) and the height was set to 2 // the bounding box of the static rigid body should reflect those values combined with the scale values above @@ -291,7 +291,7 @@ namespace PhysXEditorTests EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); // since there was no editor rigid body component, the runtime entity should have a static rigid body - const auto* staticBody = gameEntity->FindComponent()->GetStaticRigidBody(); + const auto* staticBody = azdynamic_cast(gameEntity->FindComponent()->GetSimulatedBody()); const auto* pxRigidStatic = static_cast(staticBody->GetNativePointer()); PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene()); @@ -363,7 +363,7 @@ namespace PhysXEditorTests EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); // since there was no editor rigid body component, the runtime entity should have a static rigid body - const auto* staticBody = gameEntity->FindComponent()->GetStaticRigidBody(); + const auto* staticBody = azdynamic_cast(gameEntity->FindComponent()->GetSimulatedBody()); const auto* pxRigidStatic = static_cast(staticBody->GetNativePointer()); PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene()); @@ -442,7 +442,7 @@ namespace PhysXEditorTests // make a game entity and check its bounding box is consistent with the changed transform EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); - const auto* staticBody = gameEntity->FindComponent()->GetStaticRigidBody(); + const auto* staticBody = azdynamic_cast(gameEntity->FindComponent()->GetSimulatedBody()); AZ::Aabb aabb = staticBody->GetAabb(); EXPECT_TRUE(aabb.GetMax().IsClose(translation + 0.5f * scale * boxDimensions)); EXPECT_TRUE(aabb.GetMin().IsClose(translation - 0.5f * scale * boxDimensions)); diff --git a/Gems/PhysX/Code/physx_files.cmake b/Gems/PhysX/Code/physx_files.cmake index ed2d59b8aa..6350c06e0d 100644 --- a/Gems/PhysX/Code/physx_files.cmake +++ b/Gems/PhysX/Code/physx_files.cmake @@ -102,7 +102,6 @@ set(FILES Source/PhysXCharacters/Components/CharacterGameplayComponent.h Source/PhysXCharacters/Components/RagdollComponent.cpp Source/PhysXCharacters/Components/RagdollComponent.h - Include/PhysX/Debug/PhysXDebugConfiguration.h Include/PhysX/Debug/PhysXDebugInterface.h Include/PhysX/Configuration/PhysXConfiguration.h diff --git a/Gems/PhysXDebug/Code/Source/SystemComponent.cpp b/Gems/PhysXDebug/Code/Source/SystemComponent.cpp index 77b0959008..08bf71753d 100644 --- a/Gems/PhysXDebug/Code/Source/SystemComponent.cpp +++ b/Gems/PhysXDebug/Code/Source/SystemComponent.cpp @@ -39,14 +39,9 @@ namespace PhysXDebug { const float SystemComponent::m_maxCullingBoxSize = 150.0f; - - const ColorB CreateColorFromU32(AZ::u32 color) + namespace Internal { - const AZ::u8 a = static_cast((color & 0xFF000000) >> 24); - const AZ::u8 b = static_cast((color & 0x00FF0000) >> 16); - const AZ::u8 g = static_cast((color & 0x0000FF00) >> 8); - const AZ::u8 r = static_cast(color & 0x000000FF); - return ColorB(r, g, b, a); + const AZ::Crc32 VewportId = 0; // was AzFramework::g_defaultSceneEntityDebugDisplayId but it didn't render to the viewport. } bool UseEditorPhysicsScene() @@ -338,18 +333,18 @@ namespace PhysXDebug } } - void SystemComponent::BuildColorPickingMenuItem(const AZStd::string& label, ColorB& color) + void SystemComponent::BuildColorPickingMenuItem(const AZStd::string& label, AZ::Color& color) { - float col[3] = {color.r / 255.0f, color.g / 255.0f, color.b / 255.0f}; + float col[3] = {color.GetR(), color.GetG(), color.GetB()}; if (ImGui::ColorEdit3(label.c_str(), col, ImGuiColorEditFlags_NoAlpha)) { - const float r = AZ::GetClamp(col[0] * 255.0f, 0.0f, 255.0f); - const float g = AZ::GetClamp(col[1] * 255.0f, 0.0f, 255.0f); - const float b = AZ::GetClamp(col[2] * 255.0f, 0.0f, 255.0f); + const float r = AZ::GetClamp(col[0], 0.0f, 1.0f); + const float g = AZ::GetClamp(col[1], 0.0f, 1.0f); + const float b = AZ::GetClamp(col[2], 0.0f, 1.0f); - color.r = static_cast(r); - color.g = static_cast(g); - color.b = static_cast(b); + color.SetR(r); + color.SetG(g); + color.SetB(b); } } #endif // IMGUI_ENABLED @@ -511,16 +506,34 @@ namespace PhysXDebug void SystemComponent::RenderBuffers() { - if (gEnv && gEnv->pRenderer && !m_linePoints.empty()) + if (!m_linePoints.empty() || !m_trianglePoints.empty()) { - AZ_Assert(m_linePoints.size() == m_lineColors.size(), "Lines: Expected an equal number of points to colors."); - gEnv->pRenderer->GetIRenderAuxGeom()->DrawLines(m_linePoints.begin(), m_linePoints.size(), m_lineColors.begin(), 1.0f); - } - - if (gEnv && gEnv->pRenderer && !m_trianglePoints.empty()) - { - AZ_Assert(m_trianglePoints.size() == m_triangleColors.size(), "Triangles: Expected an equal number of points to colors."); - gEnv->pRenderer->GetIRenderAuxGeom()->DrawTriangles(m_trianglePoints.begin(), m_trianglePoints.size(), m_triangleColors.begin()); + AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; + AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, Internal::VewportId); + AZ_Assert(debugDisplayBus, "Invalid DebugDisplayRequestBus."); + AzFramework::DebugDisplayRequests* debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); + if (debugDisplay) + { + if (!m_linePoints.empty()) + { + AZ_Assert(m_linePoints.size() == m_lineColors.size(), "Lines: Expected an equal number of points to colors."); + const size_t minLen = AZ::GetMin(m_linePoints.size(), m_lineColors.size()); + for (size_t i = 0; i < minLen; i += 2) + { + debugDisplay->DrawLine(m_linePoints[i], m_linePoints[i + 1], m_lineColors[i].GetAsVector4(), m_lineColors[i + 1].GetAsVector4()); + } + } + if (!m_trianglePoints.empty()) + { + AZ_Assert(m_trianglePoints.size() == m_triangleColors.size(), "Triangles: Expected an equal number of points to colors."); + const size_t minLen = AZ::GetMin(m_trianglePoints.size(), m_triangleColors.size()); + for (size_t i = 0; i < minLen; i += 3) + { + debugDisplay->SetColor(m_triangleColors[i]); + debugDisplay->DrawTri(m_trianglePoints[i], m_trianglePoints[i + 1], m_trianglePoints[i + 2]); + } + } + } } } @@ -677,8 +690,8 @@ namespace PhysXDebug if (!cameraTranslation.IsClose(AZ::Vector3::CreateZero())) { - physx::PxVec3 min = PxMathConvert(cameraTranslation - AZ::Vector3(m_culling.m_boxSize)); - physx::PxVec3 max = PxMathConvert(cameraTranslation + AZ::Vector3(m_culling.m_boxSize)); + const physx::PxVec3 min = PxMathConvert(cameraTranslation - AZ::Vector3(m_culling.m_boxSize)); + const physx::PxVec3 max = PxMathConvert(cameraTranslation + AZ::Vector3(m_culling.m_boxSize)); m_cullingBox = physx::PxBounds3(min, max); if (m_culling.m_boxWireframe) @@ -813,8 +826,8 @@ namespace PhysXDebug for (size_t lineIndex = 0; lineIndex < jointLineBufferSize / 2; lineIndex++) { - m_linePoints.emplace_back(AZVec3ToLYVec3(jointWorldTransform.TransformPoint(m_jointLineBuffer[2 * lineIndex]))); - m_linePoints.emplace_back(AZVec3ToLYVec3(jointWorldTransform.TransformPoint(m_jointLineBuffer[2 * lineIndex + 1]))); + m_linePoints.emplace_back(jointWorldTransform.TransformPoint(m_jointLineBuffer[2 * lineIndex])); + m_linePoints.emplace_back(jointWorldTransform.TransformPoint(m_jointLineBuffer[2 * lineIndex + 1])); m_lineColors.emplace_back(m_colorMappings.m_green); m_lineColors.emplace_back(m_colorMappings.m_green); } @@ -829,16 +842,21 @@ namespace PhysXDebug { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Physics); - if (gEnv && gEnv->pRenderer && m_settings.m_visualizationEnabled && m_culling.m_boxWireframe) + if (m_settings.m_visualizationEnabled && m_culling.m_boxWireframe) { - ColorB wireframeColor = MapOriginalPhysXColorToUserDefinedValues(1); - AABB lyAABB(AZAabbToLyAABB(cullingBoxAabb)); - - gEnv->pRenderer->GetIRenderAuxGeom()->DrawAABB(lyAABB, false, wireframeColor, EBoundingBoxDrawStyle::eBBD_Extremes_Color_Encoded); + AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; + AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, Internal::VewportId); + AZ_Assert(debugDisplayBus, "Invalid DebugDisplayRequestBus."); + if (AzFramework::DebugDisplayRequests* debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus)) + { + const AZ::Color wireframeColor = MapOriginalPhysXColorToUserDefinedValues(1); + debugDisplay->SetColor(wireframeColor.GetAsVector4()); + debugDisplay->DrawWireBox(cullingBoxAabb.GetMin(), cullingBoxAabb.GetMax()); + } } } - ColorB SystemComponent::MapOriginalPhysXColorToUserDefinedValues(const physx::PxU32& originalColor) + AZ::Color SystemComponent::MapOriginalPhysXColorToUserDefinedValues(const physx::PxU32& originalColor) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Physics); @@ -877,18 +895,18 @@ namespace PhysXDebug void SystemComponent::InitPhysXColorMappings() { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Physics); - m_colorMappings.m_defaultColor = CreateColorFromU32(physx::PxDebugColor::eARGB_GREEN); - m_colorMappings.m_black = CreateColorFromU32(physx::PxDebugColor::eARGB_BLACK); - m_colorMappings.m_red = CreateColorFromU32(physx::PxDebugColor::eARGB_RED); - m_colorMappings.m_green = CreateColorFromU32(physx::PxDebugColor::eARGB_GREEN); - m_colorMappings.m_blue = CreateColorFromU32(physx::PxDebugColor::eARGB_BLUE); - m_colorMappings.m_yellow = CreateColorFromU32(physx::PxDebugColor::eARGB_YELLOW); - m_colorMappings.m_magenta = CreateColorFromU32(physx::PxDebugColor::eARGB_MAGENTA); - m_colorMappings.m_cyan = CreateColorFromU32(physx::PxDebugColor::eARGB_CYAN); - m_colorMappings.m_white = CreateColorFromU32(physx::PxDebugColor::eARGB_WHITE); - m_colorMappings.m_grey = CreateColorFromU32(physx::PxDebugColor::eARGB_GREY); - m_colorMappings.m_darkRed = CreateColorFromU32(physx::PxDebugColor::eARGB_DARKRED); - m_colorMappings.m_darkGreen = CreateColorFromU32(physx::PxDebugColor::eARGB_DARKGREEN); - m_colorMappings.m_darkBlue = CreateColorFromU32(physx::PxDebugColor::eARGB_DARKBLUE); + m_colorMappings.m_defaultColor.FromU32(physx::PxDebugColor::eARGB_GREEN); + m_colorMappings.m_black.FromU32(physx::PxDebugColor::eARGB_BLACK); + m_colorMappings.m_red.FromU32(physx::PxDebugColor::eARGB_RED); + m_colorMappings.m_green.FromU32(physx::PxDebugColor::eARGB_GREEN); + m_colorMappings.m_blue.FromU32(physx::PxDebugColor::eARGB_BLUE); + m_colorMappings.m_yellow.FromU32(physx::PxDebugColor::eARGB_YELLOW); + m_colorMappings.m_magenta.FromU32(physx::PxDebugColor::eARGB_MAGENTA); + m_colorMappings.m_cyan.FromU32(physx::PxDebugColor::eARGB_CYAN); + m_colorMappings.m_white.FromU32(physx::PxDebugColor::eARGB_WHITE); + m_colorMappings.m_grey.FromU32(physx::PxDebugColor::eARGB_GREY); + m_colorMappings.m_darkRed.FromU32(physx::PxDebugColor::eARGB_DARKRED); + m_colorMappings.m_darkGreen.FromU32(physx::PxDebugColor::eARGB_DARKGREEN); + m_colorMappings.m_darkBlue.FromU32(physx::PxDebugColor::eARGB_DARKBLUE); } } diff --git a/Gems/PhysXDebug/Code/Source/SystemComponent.h b/Gems/PhysXDebug/Code/Source/SystemComponent.h index 66f546c661..631354c034 100644 --- a/Gems/PhysXDebug/Code/Source/SystemComponent.h +++ b/Gems/PhysXDebug/Code/Source/SystemComponent.h @@ -87,19 +87,19 @@ namespace PhysXDebug { AZ_RTTI(ColorMappings, "{021E40A6-568E-430A-9332-EF180DACD3C0}"); // user defined colors for physx debug primitives - ColorB m_defaultColor; - ColorB m_black; - ColorB m_red; - ColorB m_green; - ColorB m_blue; - ColorB m_yellow; - ColorB m_magenta; - ColorB m_cyan; - ColorB m_white; - ColorB m_grey; - ColorB m_darkRed; - ColorB m_darkGreen; - ColorB m_darkBlue; + AZ::Color m_defaultColor; + AZ::Color m_black; + AZ::Color m_red; + AZ::Color m_green; + AZ::Color m_blue; + AZ::Color m_yellow; + AZ::Color m_magenta; + AZ::Color m_cyan; + AZ::Color m_white; + AZ::Color m_grey; + AZ::Color m_darkRed; + AZ::Color m_darkGreen; + AZ::Color m_darkBlue; }; class SystemComponent @@ -156,7 +156,7 @@ namespace PhysXDebug /// Convert from PhysX Visualization debug colors to user defined colors. /// @param originalColor a color from the PhysX debug visualization data. /// @return a user specified color mapping (defaulting to the original PhysX color). - ColorB MapOriginalPhysXColorToUserDefinedValues(const physx::PxU32& originalColor); + AZ::Color MapOriginalPhysXColorToUserDefinedValues(const physx::PxU32& originalColor); /// Initialise the PhysX debug draw colors based on defaults. void InitPhysXColorMappings(); @@ -194,7 +194,7 @@ namespace PhysXDebug #ifdef IMGUI_ENABLED /// Build a specific color picker menu option. - void BuildColorPickingMenuItem(const AZStd::string& label, ColorB& color); + void BuildColorPickingMenuItem(const AZStd::string& label, AZ::Color& color); #endif // IMGUI_ENABLED physx::PxScene* GetCurrentPxScene(); @@ -209,10 +209,10 @@ namespace PhysXDebug bool m_editorPhysicsSceneDirty = true; static const float m_maxCullingBoxSize; - AZStd::vector m_linePoints; - AZStd::vector m_lineColors; - AZStd::vector m_trianglePoints; - AZStd::vector m_triangleColors; + AZStd::vector m_linePoints; + AZStd::vector m_lineColors; + AZStd::vector m_trianglePoints; + AZStd::vector m_triangleColors; // joint limit buffers AZStd::vector m_jointVertexBuffer; diff --git a/Gems/ScriptCanvas/Code/Editor/Components/EditorUtils.cpp b/Gems/ScriptCanvas/Code/Editor/Components/EditorUtils.cpp index e4596b5334..92aa1dddc7 100644 --- a/Gems/ScriptCanvas/Code/Editor/Components/EditorUtils.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Components/EditorUtils.cpp @@ -58,7 +58,7 @@ namespace ScriptCanvasEditor } else { - resultHash = ScriptCanvas::NodeUtils::ConstructMethodNodeIdentifier(classMethodTreeItem->GetClassMethodName(), classMethodTreeItem->GetMethodName()); + resultHash = ScriptCanvas::NodeUtils::ConstructMethodNodeIdentifier(classMethodTreeItem->GetClassMethodName(), classMethodTreeItem->GetMethodName(), classMethodTreeItem->GetPropertyStatus()); } } else if (auto globalMethodTreeItem = azrtti_cast(treeItem); globalMethodTreeItem != nullptr) diff --git a/Gems/ScriptCanvas/Code/Editor/Nodes/NodeCreateUtils.cpp b/Gems/ScriptCanvas/Code/Editor/Nodes/NodeCreateUtils.cpp index 62905947a8..07c96aba82 100644 --- a/Gems/ScriptCanvas/Code/Editor/Nodes/NodeCreateUtils.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Nodes/NodeCreateUtils.cpp @@ -167,7 +167,7 @@ namespace ScriptCanvasEditor::Nodes return nodeIdPair; } - NodeIdPair CreateObjectMethodNode(AZStd::string_view className, AZStd::string_view methodName, const ScriptCanvas::ScriptCanvasId& scriptCanvasId) + NodeIdPair CreateObjectMethodNode(AZStd::string_view className, AZStd::string_view methodName, const ScriptCanvas::ScriptCanvasId& scriptCanvasId, ScriptCanvas::PropertyStatus propertyStatus) { AZ_PROFILE_TIMER("ScriptCanvas", __FUNCTION__); NodeIdPair nodeIds; @@ -181,7 +181,7 @@ namespace ScriptCanvasEditor::Nodes auto* methodNode = azrtti_cast(node); ScriptCanvas::NamespacePath emptyNamespacePath; - methodNode->InitializeBehaviorMethod(emptyNamespacePath, className, methodName); + methodNode->InitializeBehaviorMethod(emptyNamespacePath, className, methodName, propertyStatus); AZStd::string_view displayName = methodNode->GetName(); scriptCanvasEntity->SetName(AZStd::string::format("SC-Node(%.*s)", aznumeric_cast(displayName.size()), displayName.data())); @@ -208,7 +208,7 @@ namespace ScriptCanvasEditor::Nodes auto* methodNode = azrtti_cast(node); ScriptCanvas::NamespacePath emptyNamespacePath; - methodNode->InitializeBehaviorMethod(emptyNamespacePath, className, methodName); + methodNode->InitializeBehaviorMethod(emptyNamespacePath, className, methodName, ScriptCanvas::PropertyStatus::None); AZStd::string_view displayName = methodNode->GetName(); scriptCanvasEntity->SetName(AZStd::string::format("SC-Node(%.*s)", aznumeric_cast(displayName.size()), displayName.data())); diff --git a/Gems/ScriptCanvas/Code/Editor/Nodes/NodeCreateUtils.h b/Gems/ScriptCanvas/Code/Editor/Nodes/NodeCreateUtils.h index fa81ccfe8e..f4f0378333 100644 --- a/Gems/ScriptCanvas/Code/Editor/Nodes/NodeCreateUtils.h +++ b/Gems/ScriptCanvas/Code/Editor/Nodes/NodeCreateUtils.h @@ -34,7 +34,7 @@ namespace ScriptCanvasEditor::Nodes AZStd::pair CreateAndGetNode(const AZ::Uuid& classData, const ScriptCanvas::ScriptCanvasId& scriptCanvasId, const StyleConfiguration& styleConfiguration, AZStd::function = nullptr); NodeIdPair CreateNode(const AZ::Uuid& classData, const ScriptCanvas::ScriptCanvasId& scriptCanvasId, const StyleConfiguration& styleConfiguration); NodeIdPair CreateEntityNode(const AZ::EntityId& sourceId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId); - NodeIdPair CreateObjectMethodNode(AZStd::string_view className, AZStd::string_view methodName, const ScriptCanvas::ScriptCanvasId& scriptCanvasId); + NodeIdPair CreateObjectMethodNode(AZStd::string_view className, AZStd::string_view methodName, const ScriptCanvas::ScriptCanvasId& scriptCanvasId, ScriptCanvas::PropertyStatus propertyStatus); NodeIdPair CreateObjectMethodOverloadNode(AZStd::string_view className, AZStd::string_view methodName, const ScriptCanvas::ScriptCanvasId& scriptCanvasGraphId); NodeIdPair CreateGlobalMethodNode(AZStd::string_view methodName, const ScriptCanvas::ScriptCanvasId& scriptCanvasId); NodeIdPair CreateEbusWrapperNode(AZStd::string_view busName, const ScriptCanvas::ScriptCanvasId& scriptCanvasId); diff --git a/Gems/ScriptCanvas/Code/Editor/Nodes/NodeDisplayUtils.cpp b/Gems/ScriptCanvas/Code/Editor/Nodes/NodeDisplayUtils.cpp index a903c9f885..c3929e6bc4 100644 --- a/Gems/ScriptCanvas/Code/Editor/Nodes/NodeDisplayUtils.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Nodes/NodeDisplayUtils.cpp @@ -327,12 +327,14 @@ namespace ScriptCanvasEditor::Nodes contextGroup = TranslationContextGroup::EbusSender; break; case ScriptCanvas::MethodType::Member: + case ScriptCanvas::MethodType::Getter: + case ScriptCanvas::MethodType::Setter: case ScriptCanvas::MethodType::Free: graphCanvasEntity->CreateComponent(); contextGroup = TranslationContextGroup::ClassMethod; break; default: - AZ_Error("ScriptCanvas", false, "Invalid method node type, node creation failed. This node nodes to be deleted."); + AZ_Error("ScriptCanvas", false, "Invalid method node type, node creation failed. This node needs to be deleted."); break; } diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/EBusNodePaletteTreeItemTypes.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/EBusNodePaletteTreeItemTypes.cpp index 4ae7d94560..474af058d3 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/EBusNodePaletteTreeItemTypes.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/EBusNodePaletteTreeItemTypes.cpp @@ -52,14 +52,16 @@ namespace ScriptCanvasEditor ->Field("BusName", &CreateEBusSenderMimeEvent::m_busName) ->Field("EventName", &CreateEBusSenderMimeEvent::m_eventName) ->Field("IsOverload", &CreateEBusSenderMimeEvent::m_isOverload) + ->Field("propertyStatus", &CreateEBusSenderMimeEvent::m_propertyStatus) ; } } - CreateEBusSenderMimeEvent::CreateEBusSenderMimeEvent(AZStd::string_view busName, AZStd::string_view eventName, bool isOverload) + CreateEBusSenderMimeEvent::CreateEBusSenderMimeEvent(AZStd::string_view busName, AZStd::string_view eventName, bool isOverload, ScriptCanvas::PropertyStatus propertyStatus) : m_busName(busName.data()) , m_eventName(eventName.data()) , m_isOverload(isOverload) + , m_propertyStatus(propertyStatus) { } @@ -71,7 +73,7 @@ namespace ScriptCanvasEditor } else { - return Nodes::CreateObjectMethodNode(m_busName, m_eventName, scriptCanvasId); + return Nodes::CreateObjectMethodNode(m_busName, m_eventName, scriptCanvasId, m_propertyStatus); } } @@ -91,13 +93,14 @@ namespace ScriptCanvasEditor return defaultIcon; } - EBusSendEventPaletteTreeItem::EBusSendEventPaletteTreeItem(AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busIdentifier, const ScriptCanvas::EBusEventId& eventIdentifier, bool isOverload) + EBusSendEventPaletteTreeItem::EBusSendEventPaletteTreeItem(AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busIdentifier, const ScriptCanvas::EBusEventId& eventIdentifier, bool isOverload, ScriptCanvas::PropertyStatus propertyStatus) : DraggableNodePaletteTreeItem(eventName, ScriptCanvasEditor::AssetEditorId) , m_busName(busName.data()) , m_eventName(eventName.data()) , m_busId(busIdentifier) , m_eventId(eventIdentifier) , m_isOverload(isOverload) + , m_propertyStatus(propertyStatus) { AZStd::string displayEventName = TranslationHelper::GetKeyTranslation(TranslationContextGroup::EbusSender, m_busName.toUtf8().data(), m_eventName.toUtf8().data(), TranslationItemType::Node, TranslationKeyId::Name); @@ -122,7 +125,7 @@ namespace ScriptCanvasEditor GraphCanvas::GraphCanvasMimeEvent* EBusSendEventPaletteTreeItem::CreateMimeEvent() const { - return aznew CreateEBusSenderMimeEvent(m_busName.toUtf8().data(), m_eventName.toUtf8().data(), m_isOverload); + return aznew CreateEBusSenderMimeEvent(m_busName.toUtf8().data(), m_eventName.toUtf8().data(), m_isOverload, ScriptCanvas::PropertyStatus::None); } AZStd::string EBusSendEventPaletteTreeItem::GetBusName() const @@ -145,6 +148,11 @@ namespace ScriptCanvasEditor return m_eventId; } + ScriptCanvas::PropertyStatus EBusSendEventPaletteTreeItem::GetPropertyStatus() const + { + return m_propertyStatus; + } + bool EBusSendEventPaletteTreeItem::IsOverload() const { return m_isOverload; diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/EBusNodePaletteTreeItemTypes.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/EBusNodePaletteTreeItemTypes.h index b5181754c4..b3bd771109 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/EBusNodePaletteTreeItemTypes.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/EBusNodePaletteTreeItemTypes.h @@ -28,7 +28,7 @@ namespace ScriptCanvasEditor static void Reflect(AZ::ReflectContext* reflectContext); CreateEBusSenderMimeEvent() = default; - CreateEBusSenderMimeEvent(AZStd::string_view busName, AZStd::string_view eventName, bool isOverload); + CreateEBusSenderMimeEvent(AZStd::string_view busName, AZStd::string_view eventName, bool isOverload, ScriptCanvas::PropertyStatus propertyStatus); ~CreateEBusSenderMimeEvent() = default; protected: @@ -36,6 +36,7 @@ namespace ScriptCanvasEditor private: bool m_isOverload; + ScriptCanvas::PropertyStatus m_propertyStatus = ScriptCanvas::PropertyStatus::None; AZStd::string m_busName; AZStd::string m_eventName; }; @@ -50,7 +51,7 @@ namespace ScriptCanvasEditor AZ_CLASS_ALLOCATOR(EBusSendEventPaletteTreeItem, AZ::SystemAllocator, 0); AZ_RTTI(EBusSendEventPaletteTreeItem, "{26258B0A-8E2C-434D-ACAD-3DE85E64A4F8}", GraphCanvas::DraggableNodePaletteTreeItem); - EBusSendEventPaletteTreeItem(AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const ScriptCanvas::EBusEventId& eventIdentifier, bool isOverload); + EBusSendEventPaletteTreeItem(AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const ScriptCanvas::EBusEventId& eventIdentifier, bool isOverload, ScriptCanvas::PropertyStatus propertyStatus); ~EBusSendEventPaletteTreeItem() = default; GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override; @@ -63,6 +64,8 @@ namespace ScriptCanvasEditor bool IsOverload() const; + ScriptCanvas::PropertyStatus GetPropertyStatus() const; + private: bool m_isOverload; QString m_busName; @@ -70,6 +73,7 @@ namespace ScriptCanvasEditor ScriptCanvas::EBusBusId m_busId; ScriptCanvas::EBusEventId m_eventId; + ScriptCanvas::PropertyStatus m_propertyStatus = ScriptCanvas::PropertyStatus::None; }; // diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/GeneralNodePaletteTreeItemTypes.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/GeneralNodePaletteTreeItemTypes.cpp index 912a76df22..8341303ff4 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/GeneralNodePaletteTreeItemTypes.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/GeneralNodePaletteTreeItemTypes.cpp @@ -51,14 +51,16 @@ namespace ScriptCanvasEditor ->Field("ClassName", &CreateClassMethodMimeEvent::m_className) ->Field("MethodName", &CreateClassMethodMimeEvent::m_methodName) ->Field("IsOverload", &CreateClassMethodMimeEvent::m_isOverload) + ->Field("propertyStatus", &CreateClassMethodMimeEvent::m_propertyStatus) ; } } - CreateClassMethodMimeEvent::CreateClassMethodMimeEvent(const QString& className, const QString& methodName, bool isOverload) + CreateClassMethodMimeEvent::CreateClassMethodMimeEvent(const QString& className, const QString& methodName, bool isOverload, ScriptCanvas::PropertyStatus propertyStatus) : m_className(className.toUtf8().data()) , m_methodName(methodName.toUtf8().data()) , m_isOverload(isOverload) + , m_propertyStatus(propertyStatus) { } @@ -70,7 +72,7 @@ namespace ScriptCanvasEditor } else { - return Nodes::CreateObjectMethodNode(m_className, m_methodName, scriptCanvasId); + return Nodes::CreateObjectMethodNode(m_className, m_methodName, scriptCanvasId, m_propertyStatus); } } @@ -78,11 +80,12 @@ namespace ScriptCanvasEditor // ClassMethodEventPaletteTreeItem //////////////////////////////////// - ClassMethodEventPaletteTreeItem::ClassMethodEventPaletteTreeItem(AZStd::string_view className, AZStd::string_view methodName, bool isOverload) + ClassMethodEventPaletteTreeItem::ClassMethodEventPaletteTreeItem(AZStd::string_view className, AZStd::string_view methodName, bool isOverload, ScriptCanvas::PropertyStatus propertyStatus) : DraggableNodePaletteTreeItem(methodName, ScriptCanvasEditor::AssetEditorId) , m_className(className.data()) , m_methodName(methodName.data()) , m_isOverload(isOverload) + , m_propertyStatus(propertyStatus) { AZStd::string displayMethodName = TranslationHelper::GetKeyTranslation(TranslationContextGroup::ClassMethod, m_className.toUtf8().data(), m_methodName.toUtf8().data(), TranslationItemType::Node, TranslationKeyId::Name); @@ -95,6 +98,15 @@ namespace ScriptCanvasEditor SetName(displayMethodName.c_str()); } + if (propertyStatus == ScriptCanvas::PropertyStatus::Getter) + { + SetName(AZStd::string::format("Get %s", GetName().toUtf8().data()).data()); + } + else if (propertyStatus == ScriptCanvas::PropertyStatus::Setter) + { + SetName(AZStd::string::format("Set %s", GetName().toUtf8().data()).data()); + } + AZStd::string displayEventTooltip = TranslationHelper::GetKeyTranslation(TranslationContextGroup::ClassMethod, m_className.toUtf8().data(), m_methodName.toUtf8().data(), TranslationItemType::Node, TranslationKeyId::Tooltip); if (!displayEventTooltip.empty()) @@ -107,7 +119,7 @@ namespace ScriptCanvasEditor GraphCanvas::GraphCanvasMimeEvent* ClassMethodEventPaletteTreeItem::CreateMimeEvent() const { - return aznew CreateClassMethodMimeEvent(m_className, m_methodName, m_isOverload); + return aznew CreateClassMethodMimeEvent(m_className, m_methodName, m_isOverload, m_propertyStatus); } AZStd::string ClassMethodEventPaletteTreeItem::GetClassMethodName() const @@ -125,6 +137,11 @@ namespace ScriptCanvasEditor return m_isOverload; } + ScriptCanvas::PropertyStatus ClassMethodEventPaletteTreeItem::GetPropertyStatus() const + { + return m_propertyStatus; + } + //! Implementation of the CreateGlobalMethod Mime Event void CreateGlobalMethodMimeEvent::Reflect(AZ::ReflectContext* reflectContext) { diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/GeneralNodePaletteTreeItemTypes.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/GeneralNodePaletteTreeItemTypes.h index 38a2be88f5..66b29ef3b9 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/GeneralNodePaletteTreeItemTypes.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/GeneralNodePaletteTreeItemTypes.h @@ -30,7 +30,7 @@ namespace ScriptCanvasEditor static void Reflect(AZ::ReflectContext* reflectContext); CreateClassMethodMimeEvent() = default; - CreateClassMethodMimeEvent(const QString& className, const QString& methodName, bool isOverload); + CreateClassMethodMimeEvent(const QString& className, const QString& methodName, bool isOverload, ScriptCanvas::PropertyStatus); ~CreateClassMethodMimeEvent() = default; protected: @@ -40,6 +40,7 @@ namespace ScriptCanvasEditor bool m_isOverload = false; AZStd::string m_className; AZStd::string m_methodName; + ScriptCanvas::PropertyStatus m_propertyStatus = ScriptCanvas::PropertyStatus::None; }; class ClassMethodEventPaletteTreeItem @@ -49,7 +50,7 @@ namespace ScriptCanvasEditor AZ_CLASS_ALLOCATOR(ClassMethodEventPaletteTreeItem, AZ::SystemAllocator, 0); AZ_RTTI(ClassMethodEventPaletteTreeItem, "{96F93970-F38A-4F08-8DC5-D52FCCE34E25}", GraphCanvas::DraggableNodePaletteTreeItem); - ClassMethodEventPaletteTreeItem(AZStd::string_view className, AZStd::string_view methodName, bool isOverload); + ClassMethodEventPaletteTreeItem(AZStd::string_view className, AZStd::string_view methodName, bool isOverload, ScriptCanvas::PropertyStatus propertyStatus); ~ClassMethodEventPaletteTreeItem() = default; GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override; @@ -57,11 +58,13 @@ namespace ScriptCanvasEditor AZStd::string GetClassMethodName() const; AZStd::string GetMethodName() const; bool IsOverload() const; + ScriptCanvas::PropertyStatus GetPropertyStatus() const; private: bool m_isOverload = false; QString m_className; QString m_methodName; + ScriptCanvas::PropertyStatus m_propertyStatus = ScriptCanvas::PropertyStatus::None; }; // diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp index 73b6551e85..8b00b5b71b 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.cpp @@ -129,6 +129,7 @@ namespace , const AZ::BehaviorClass* behaviorClass , const AZStd::string& name , const AZ::BehaviorMethod& method + , ScriptCanvas::PropertyStatus propertyStatus , bool isOverloaded) { if (IsDeprecated(method.m_attributes)) @@ -170,7 +171,7 @@ namespace serializeContext->RegisterType(resultParameter->m_typeId, AZStd::move(classData), EventPlaceholderAnyCreator); } - nodePaletteModel.RegisterClassNode(categoryPath, behaviorClass ? behaviorClass->m_name : "", name, &method, &behaviorContext, isOverloaded); + nodePaletteModel.RegisterClassNode(categoryPath, behaviorClass ? behaviorClass->m_name : "", name, &method, &behaviorContext, propertyStatus, isOverloaded); } void RegisterGlobalMethod(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel, const AZ::BehaviorContext& behaviorContext, @@ -556,6 +557,19 @@ namespace categoryPath.append(displayName.c_str()); } + for (auto property : behaviorClass->m_properties) + { + if (property.second->m_getter) + { + RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, property.first, *property.second->m_getter, ScriptCanvas::PropertyStatus::Getter, behaviorClass->IsMethodOverloaded(property.first)); + } + + if (property.second->m_setter) + { + RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, property.first, *property.second->m_setter, ScriptCanvas::PropertyStatus::Setter, behaviorClass->IsMethodOverloaded(property.first)); + } + } + for (auto methodIter : behaviorClass->m_methods) { if (!IsExplicitOverload(*methodIter.second)) @@ -567,7 +581,7 @@ namespace continue; } - RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, methodIter.first, *methodIter.second, behaviorClass->IsMethodOverloaded(methodIter.first)); + RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, methodIter.first, *methodIter.second, ScriptCanvas::PropertyStatus::None, behaviorClass->IsMethodOverloaded(methodIter.first)); } } } @@ -579,7 +593,7 @@ namespace { for (const AZ::ExplicitOverloadInfo& explicitOverload : behaviorContext.m_explicitOverloads) { - RegisterMethod(nodePaletteModel, behaviorContext, explicitOverload.m_categoryPath, nullptr, explicitOverload.m_name, *explicitOverload.m_overloads.begin()->first, true); + RegisterMethod(nodePaletteModel, behaviorContext, explicitOverload.m_categoryPath, nullptr, explicitOverload.m_name, *explicitOverload.m_overloads.begin()->first, ScriptCanvas::PropertyStatus::None, true); } } @@ -717,7 +731,7 @@ namespace } const bool isOverload{ false }; // overloaded events are not trivially supported - nodePaletteModel.RegisterEBusSenderNodeModelInformation(categoryPath, behaviorEbus.m_name, event.first, ScriptCanvas::EBusBusId(behaviorEbus.m_name.c_str()), ScriptCanvas::EBusEventId(event.first.c_str()), event.second, isOverload); + nodePaletteModel.RegisterEBusSenderNodeModelInformation(categoryPath, behaviorEbus.m_name, event.first, ScriptCanvas::EBusBusId(behaviorEbus.m_name.c_str()), ScriptCanvas::EBusEventId(event.first.c_str()), event.second, ScriptCanvas::PropertyStatus::None, isOverload); } } } @@ -1024,11 +1038,16 @@ namespace ScriptCanvasEditor } } - void NodePaletteModel::RegisterClassNode(const AZStd::string& categoryPath, const AZStd::string& methodClass, - const AZStd::string& methodName, const AZ::BehaviorMethod* behaviorMethod, const AZ::BehaviorContext* behaviorContext, - bool isOverload) + void NodePaletteModel::RegisterClassNode + ( const AZStd::string& categoryPath + , const AZStd::string& methodClass + , const AZStd::string& methodName + , const AZ::BehaviorMethod* behaviorMethod + , const AZ::BehaviorContext* behaviorContext + , ScriptCanvas::PropertyStatus propertyStatus + , bool isOverload) { - ScriptCanvas::NodeTypeIdentifier nodeIdentifier = isOverload ? ScriptCanvas::NodeUtils::ConstructMethodOverloadedNodeIdentifier(methodName) : ScriptCanvas::NodeUtils::ConstructMethodNodeIdentifier(methodClass, methodName); + ScriptCanvas::NodeTypeIdentifier nodeIdentifier = isOverload ? ScriptCanvas::NodeUtils::ConstructMethodOverloadedNodeIdentifier(methodName) : ScriptCanvas::NodeUtils::ConstructMethodNodeIdentifier(methodClass, methodName, propertyStatus); auto registerIter = m_registeredNodes.find(nodeIdentifier); @@ -1039,7 +1058,7 @@ namespace ScriptCanvasEditor methodModelInformation->m_nodeIdentifier = nodeIdentifier; methodModelInformation->m_classMethod = methodClass; methodModelInformation->m_methodName = methodName; - + methodModelInformation->m_propertyStatus = propertyStatus; methodModelInformation->m_titlePaletteOverride = "MethodNodeTitlePalette"; methodModelInformation->m_displayName = TranslationHelper::GetKeyTranslation(TranslationContextGroup::ClassMethod, methodClass.c_str(), methodName.c_str(), TranslationItemType::Node, TranslationKeyId::Name); @@ -1198,7 +1217,15 @@ namespace ScriptCanvasEditor } } - void NodePaletteModel::RegisterEBusSenderNodeModelInformation(AZStd::string_view categoryPath, AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const ScriptCanvas::EBusEventId& eventId, const AZ::BehaviorEBusEventSender&, bool isOverload) + void NodePaletteModel::RegisterEBusSenderNodeModelInformation + ( AZStd::string_view categoryPath + , AZStd::string_view busName + , AZStd::string_view eventName + , const ScriptCanvas::EBusBusId& busId + , const ScriptCanvas::EBusEventId& eventId + , const AZ::BehaviorEBusEventSender& + , ScriptCanvas::PropertyStatus propertyStatus + , bool isOverload) { ScriptCanvas::NodeTypeIdentifier nodeIdentifier = isOverload ? ScriptCanvas::NodeUtils::ConstructEBusEventSenderOverloadedIdentifier(busId, eventId) : ScriptCanvas::NodeUtils::ConstructEBusEventSenderIdentifier(busId, eventId); @@ -1212,6 +1239,7 @@ namespace ScriptCanvasEditor senderInformation->m_titlePaletteOverride = "MethodNodeTitlePalette"; senderInformation->m_categoryPath = categoryPath; senderInformation->m_nodeIdentifier = nodeIdentifier; + senderInformation->m_propertyStatus = propertyStatus; senderInformation->m_busName = busName; senderInformation->m_eventName = eventName; diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.h b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.h index 5ed0ff670b..3bc81c07a0 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.h +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/NodePalette/NodePaletteModel.h @@ -83,12 +83,12 @@ namespace ScriptCanvasEditor void RepopulateModel(); void RegisterCustomNode(AZStd::string_view categoryPath, const AZ::Uuid& uuid, AZStd::string_view name, const AZ::SerializeContext::ClassData* classData); - void RegisterClassNode(const AZStd::string& categoryPath, const AZStd::string& methodClass, const AZStd::string& methodName, const AZ::BehaviorMethod* behaviorMethod, const AZ::BehaviorContext* behaviorContext, bool isOverload); + void RegisterClassNode(const AZStd::string& categoryPath, const AZStd::string& methodClass, const AZStd::string& methodName, const AZ::BehaviorMethod* behaviorMethod, const AZ::BehaviorContext* behaviorContext, ScriptCanvas::PropertyStatus propertyStatus, bool isOverload); void RegisterMethodNode(const AZ::BehaviorContext& behaviorContext, const AZ::BehaviorMethod& behaviorMethod); void RegisterGlobalConstant(const AZ::BehaviorContext& behaviorContext, const AZ::BehaviorMethod& behaviorMethod); void RegisterEBusHandlerNodeModelInformation(AZStd::string_view categoryPath, AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const AZ::BehaviorEBusHandler::BusForwarderEvent& forwardEvent); - void RegisterEBusSenderNodeModelInformation(AZStd::string_view categoryPath, AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const ScriptCanvas::EBusEventId& eventId, const AZ::BehaviorEBusEventSender& eventSender, bool isOverload); + void RegisterEBusSenderNodeModelInformation(AZStd::string_view categoryPath, AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const ScriptCanvas::EBusEventId& eventId, const AZ::BehaviorEBusEventSender& eventSender, ScriptCanvas::PropertyStatus propertyStatus, bool isOverload); // Asset Based Registrations AZStd::vector RegisterScriptEvent(ScriptEvents::ScriptEventsAsset* scriptEventAsset); @@ -164,6 +164,7 @@ namespace ScriptCanvasEditor bool m_isOverload{}; AZStd::string m_classMethod; AZStd::string m_methodName; + ScriptCanvas::PropertyStatus m_propertyStatus = ScriptCanvas::PropertyStatus::None; }; struct GlobalMethodNodeModelInformation @@ -202,6 +203,7 @@ namespace ScriptCanvasEditor ScriptCanvas::EBusBusId m_busId; ScriptCanvas::EBusEventId m_eventId; + ScriptCanvas::PropertyStatus m_propertyStatus = ScriptCanvas::PropertyStatus::None; }; struct ScriptEventHandlerNodeModelInformation diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp index 5d377fe241..062db5e315 100644 --- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp +++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.cpp @@ -111,7 +111,7 @@ namespace ScriptCanvasEditor } else if (auto methodNodeModelInformation = azrtti_cast(modelInformation)) { - createdItem = parentItem->CreateChildNode(methodNodeModelInformation->m_classMethod, methodNodeModelInformation->m_methodName, methodNodeModelInformation->m_isOverload); + createdItem = parentItem->CreateChildNode(methodNodeModelInformation->m_classMethod, methodNodeModelInformation->m_methodName, methodNodeModelInformation->m_isOverload, methodNodeModelInformation->m_propertyStatus); } else if (auto globalMethodNodeModelInformation = azrtti_cast(modelInformation); globalMethodNodeModelInformation != nullptr) @@ -130,7 +130,7 @@ namespace ScriptCanvasEditor { if (!azrtti_istypeof(ebusSenderNodeModelInformation)) { - createdItem = parentItem->CreateChildNode(ebusSenderNodeModelInformation->m_busName, ebusSenderNodeModelInformation->m_eventName, ebusSenderNodeModelInformation->m_busId, ebusSenderNodeModelInformation->m_eventId, ebusSenderNodeModelInformation->m_isOverload); + createdItem = parentItem->CreateChildNode(ebusSenderNodeModelInformation->m_busName, ebusSenderNodeModelInformation->m_eventName, ebusSenderNodeModelInformation->m_busId, ebusSenderNodeModelInformation->m_eventId, ebusSenderNodeModelInformation->m_isOverload, ebusSenderNodeModelInformation->m_propertyStatus); } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h index d6ac7e5482..5b1e5eee76 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h @@ -88,6 +88,13 @@ namespace ScriptCanvas Current, }; + enum class PropertyStatus : AZ::u8 + { + Getter, + None, + Setter, + }; + struct VersionData { AZ_TYPE_INFO(VersionData, "{14C629F6-467B-46FE-8B63-48FDFCA42175}"); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/MethodConfiguration.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/MethodConfiguration.h index be1f4b39b8..049404ee6e 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/MethodConfiguration.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/MethodConfiguration.h @@ -33,6 +33,8 @@ namespace ScriptCanvas Event, Free, Member, + Getter, + Setter, Count, }; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h index d3d36ae71b..a2d06686e9 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h @@ -394,6 +394,7 @@ namespace ScriptCanvas AZ::Uuid m_type = AZ::Uuid::CreateNull(); AZStd::string m_className; AZStd::string m_methodName; + PropertyStatus m_propertyStatus = PropertyStatus::None; bool IsValid() { diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Nodeable.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Nodeable.cpp index b642ef3889..83680aa7de 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Nodeable.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Nodeable.cpp @@ -104,8 +104,7 @@ namespace ScriptCanvas const FunctorOut& Nodeable::GetExecutionOutChecked(size_t index) const { - - if (index >= m_outs.size() && m_outs[index]) + if (index >= m_outs.size() || !m_outs[index]) { return m_noOpFunctor; } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.cpp index a5a98b5444..e16a3bc815 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.cpp @@ -537,6 +537,20 @@ namespace ScriptCanvas return azrtti_istypeof(execution->GetId().m_node); } + bool IsClassPropertyRead(ExecutionTreeConstPtr execution) + { + return execution->GetSymbol() == Symbol::FunctionCall + && azrtti_istypeof(execution->GetId().m_node) + && azrtti_cast(execution->GetId().m_node)->GetPropertyStatus() == PropertyStatus::Getter; + } + + bool IsClassPropertyWrite(ExecutionTreeConstPtr execution) + { + return execution->GetSymbol() == Symbol::FunctionCall + && azrtti_istypeof(execution->GetId().m_node) + && azrtti_cast(execution->GetId().m_node)->GetPropertyStatus() == PropertyStatus::Setter; + } + bool IsCodeConstructable(Grammar::VariableConstPtr value) { return Data::IsValueType(value->m_datum.GetType()) @@ -1280,7 +1294,6 @@ namespace ScriptCanvas return identifier; } - ExecutionTraversalResult TraverseExecutionConnectionsRecurse(const EndpointsResolved& nextEndpoints, AZStd::unordered_set& previousIns, GraphExecutionPathTraversalListener& listener); ExecutionTraversalResult TraverseExecutionConnectionsRecurse(const EndpointResolved& in, AZStd::unordered_set& previousIns, GraphExecutionPathTraversalListener& listener); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.h index 1e2d102d2d..b5fd606fba 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.h @@ -79,6 +79,10 @@ namespace ScriptCanvas bool IsBreak(const ExecutionTreeConstPtr& execution); + bool IsClassPropertyRead(ExecutionTreeConstPtr execution); + + bool IsClassPropertyWrite(ExecutionTreeConstPtr execution); + bool IsCodeConstructable(VariableConstPtr value); bool IsCycle(const Node& node); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp index 836c3facab..b6f7bc2972 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.cpp @@ -193,6 +193,22 @@ namespace ScriptCanvas return DynamicDataType::Any; } + PropertyStatus Method::GetPropertyStatus() const + { + switch (m_methodType) + { + case MethodType::Getter: + return PropertyStatus::Getter; + + case MethodType::Setter: + return PropertyStatus::Setter; + + default: + return PropertyStatus::None; + } + } + + void Method::InitializeMethod(const MethodConfiguration& config) { m_namespaces = config.m_namespaces ? *config.m_namespaces : m_namespaces; @@ -239,7 +255,7 @@ namespace ScriptCanvas OnInitializeOutputPost(outputConfig); } - void Method::InitializeBehaviorMethod(const NamespacePath& namespaces, AZStd::string_view className, AZStd::string_view methodName) + void Method::InitializeBehaviorMethod(const NamespacePath& namespaces, AZStd::string_view className, AZStd::string_view methodName, PropertyStatus propertyStatus) { AZ::BehaviorContext* behaviorContext = nullptr; AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext); @@ -255,13 +271,13 @@ namespace ScriptCanvas { InitializeFree(namespaces, methodName); } - else if (auto ebusIterator = behaviorContext->m_ebuses.find(className); ebusIterator == behaviorContext->m_ebuses.end()) + else if (auto ebusIterator = behaviorContext->m_ebuses.find(className); ebusIterator != behaviorContext->m_ebuses.end()) { - InitializeClass(namespaces, className, methodName); + InitializeEvent(namespaces, className, methodName); } else { - InitializeEvent(namespaces, className, methodName); + InitializeClass(namespaces, className, methodName, propertyStatus); } } @@ -291,7 +307,7 @@ namespace ScriptCanvas } } - void Method::InitializeClass(const NamespacePath&, AZStd::string_view className, AZStd::string_view methodName) + void Method::InitializeClass(const NamespacePath&, AZStd::string_view className, AZStd::string_view methodName, PropertyStatus propertyStatus) { AZStd::lock_guard lock(m_mutex); @@ -299,9 +315,11 @@ namespace ScriptCanvas const AZ::BehaviorClass* bcClass{}; AZStd::string prettyClassName; - if (BehaviorContextUtils::FindClass(method, bcClass, className, methodName, &prettyClassName)) + if (BehaviorContextUtils::FindClass(method, bcClass, className, methodName, propertyStatus, &prettyClassName)) { - MethodConfiguration config(*method, MethodType::Member); + const auto methodType = propertyStatus == PropertyStatus::None ? MethodType::Member : propertyStatus == PropertyStatus::Getter ? MethodType::Getter : MethodType::Setter; + + MethodConfiguration config(*method, methodType); config.m_class = bcClass; config.m_namespaces = &m_namespaces; config.m_className = &className; @@ -647,8 +665,12 @@ namespace ScriptCanvas break; case MethodType::Member: + case MethodType::Getter: + case MethodType::Setter: { - if (BehaviorContextUtils::FindClass(method, bcClass, m_className, methodName, nullptr, m_warnOnMissingFunction)) + PropertyStatus status = m_methodType == MethodType::Getter ? PropertyStatus::Getter : m_methodType == MethodType::Setter ? PropertyStatus::Setter : PropertyStatus::None; + + if (BehaviorContextUtils::FindClass(method, bcClass, m_className, methodName, status, nullptr, m_warnOnMissingFunction)) { outClass = bcClass; outMethod = method; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h index b03e3aefd8..6a53054ddf 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Libraries/Core/Method.h @@ -87,14 +87,13 @@ namespace ScriptCanvas bool IsObjectClass(AZStd::string_view objectClass) const { return objectClass.compare(m_className) == 0; } //! Attempts to initialize node with a BehaviorContext BehaviorMethod - //! If the className is empty, then the methodName is searched on the BehaviorContext - //! If className is not empty the className is used to look for a registered BehaviorEBus in the BehaviorContext - //! and if found, the methodName is searched among the BehaviorEBus events - //! Otherwise the className is used to look for a registered BehaviorClass in the BehaviorContext - //! and if found, the methodName is searched among the BehaviorClass methods - void InitializeBehaviorMethod(const NamespacePath& namespaces, AZStd::string_view className, AZStd::string_view methodName); + //! 1) If the names match an overloaded method, including one using ExplicitOverloadInfo, then that method is used. Else: + //! 2) If the class name is empty, then search for a free method is searched for in the BehaviorContext and there is a warning if not found. + //! 3) If the class name matches an ebus, methodName is searched among the BehaviorEBus events, and there is a warning if not found. + //! 4) if the class name does NOT match an ebus, className and methodName are used to look for a registered BehaviorClass in the BehaviorContext, and there is a warning if not found. + void InitializeBehaviorMethod(const NamespacePath& namespaces, AZStd::string_view className, AZStd::string_view methodName, PropertyStatus propertyStatus); - void InitializeClass(const NamespacePath& namespaces, AZStd::string_view className, AZStd::string_view methodName); + void InitializeClass(const NamespacePath& namespaces, AZStd::string_view className, AZStd::string_view methodName, PropertyStatus propertyStatus); void InitializeEvent(const NamespacePath& namespaces, AZStd::string_view busName, AZStd::string_view eventName); @@ -126,6 +125,8 @@ namespace ScriptCanvas virtual DynamicDataType GetOverloadedOutputType(size_t resultIndex) const; + PropertyStatus GetPropertyStatus() const; + protected: void ConfigureMethod(const AZ::BehaviorMethod& method, const AZ::BehaviorClass* bcClass); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.cpp index 128ef95b63..905f68604a 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.cpp @@ -637,6 +637,16 @@ namespace ScriptCanvas { WriteGlobalPropertyRead(execution); } + else if (Grammar::IsClassPropertyRead(execution)) + { + WriteClassPropertyRead(execution); + m_dotLua.WriteNewLine(); + } + else if (Grammar::IsClassPropertyWrite(execution)) + { + WriteClassPropertyWrite(execution); + m_dotLua.WriteNewLine(); + } else { const bool isNullCheckRequired = Grammar::IsFunctionCallNullCheckRequired(execution); @@ -1208,6 +1218,19 @@ namespace ScriptCanvas TranslateNodeableParse(); } + void GraphToLua::WriteClassPropertyRead(Grammar::ExecutionTreeConstPtr execution) + { + WriteFunctionCallInput(execution, 0, IsFormatStringInput::No); + m_dotLua.Write(".%s", Grammar::ToIdentifier(execution->GetName()).c_str()); + } + + void GraphToLua::WriteClassPropertyWrite(Grammar::ExecutionTreeConstPtr execution) + { + WriteClassPropertyRead(execution); + m_dotLua.Write(" = "); + WriteFunctionCallInput(execution, 1, IsFormatStringInput::No); + } + void GraphToLua::WriteConditionalCaseSwitch(Grammar::ExecutionTreeConstPtr execution, Grammar::Symbol symbol, const Grammar::ExecutionChild& child, size_t index) { if (symbol == Grammar::Symbol::RandomSwitch) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.h index 3d4379d98d..82c865da8c 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.h @@ -116,6 +116,8 @@ namespace ScriptCanvas void TranslateNodeableParse(); void TranslateStaticInitialization(); void TranslateVariableInitialization(AZStd::string_view leftValue); + void WriteClassPropertyRead(Grammar::ExecutionTreeConstPtr); + void WriteClassPropertyWrite(Grammar::ExecutionTreeConstPtr); void WriteConditionalCaseSwitch(Grammar::ExecutionTreeConstPtr execution, Grammar::Symbol symbol, const Grammar::ExecutionChild& child, size_t index); enum class IsLeadingCommaRequired { No, Yes }; void WriteConstructionArgs(); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/BehaviorContextUtils.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/BehaviorContextUtils.cpp index ff38b74d47..3689729338 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/BehaviorContextUtils.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/BehaviorContextUtils.cpp @@ -61,7 +61,7 @@ namespace ScriptCanvas return { typeID }; } - bool BehaviorContextUtils::FindClass(const AZ::BehaviorMethod*& outMethod, const AZ::BehaviorClass*& outClass, [[maybe_unused]] AZStd::string_view className, [[maybe_unused]] AZStd::string_view methodName, [[maybe_unused]] AZStd::string* outPrettyClassName, [[maybe_unused]] bool warnOnMissing) + bool BehaviorContextUtils::FindClass(const AZ::BehaviorMethod*& outMethod, const AZ::BehaviorClass*& outClass, [[maybe_unused]] AZStd::string_view className, [[maybe_unused]] AZStd::string_view methodName, PropertyStatus propertyStatus, [[maybe_unused]] AZStd::string* outPrettyClassName, [[maybe_unused]] bool warnOnMissing) { AZ::BehaviorContext* behaviorContext(nullptr); AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext); @@ -81,16 +81,36 @@ namespace ScriptCanvas const AZ::BehaviorClass* behaviorClass(classIter->second); AZ_Assert(behaviorClass, "BehaviorContext Class entry %s has no class pointer", className.data()); - const auto methodIter(behaviorClass->m_methods.find(methodName.data())); - if (methodIter == behaviorClass->m_methods.end()) + + AZ::BehaviorMethod* method{}; + + if (propertyStatus == PropertyStatus::None) { - AZ_Warning("Script Canvas", !warnOnMissing, "No method by name of %s found in BehaviorContext class %s", methodName.data(), className.data()); - return false; + const auto methodIter(behaviorClass->m_methods.find(methodName.data())); + if (methodIter != behaviorClass->m_methods.end()) + { + method = methodIter->second; + propertyStatus = PropertyStatus::None; + } + else + { + AZ_Warning("Script Canvas", !warnOnMissing, "No method by name of %s found in BehaviorContext class %s", methodName.data(), className.data()); + } + } + else + { + const auto propertyIter(behaviorClass->m_properties.find(methodName.data())); + if (propertyIter == behaviorClass->m_properties.end()) + { + AZ_Warning("Script Canvas", !warnOnMissing, "No property by name of %s found in BehaviorContext class %s", methodName.data(), className.data()); + return false; + } + + method = propertyStatus == PropertyStatus::Getter ? propertyIter->second->m_getter : propertyIter->second->m_setter; } // this argument is the first argument...so perhaps remove the distinction between class and member functions, since it probably won't follow polymorphism // if it will, keep the distinction, and add the first argument separately - AZ::BehaviorMethod* method(methodIter->second); if (!method) { AZ_Warning("Script Canvas", !warnOnMissing, "BehaviorContext Method entry %s has no method pointer", methodName.data()); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/BehaviorContextUtils.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/BehaviorContextUtils.h index 057c99a774..8abe26dfd1 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/BehaviorContextUtils.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/BehaviorContextUtils.h @@ -21,7 +21,7 @@ namespace ScriptCanvas class BehaviorContextUtils { public: - static bool FindClass(const AZ::BehaviorMethod*& outMethod, const AZ::BehaviorClass*& outClass, AZStd::string_view className, AZStd::string_view methodName, AZStd::string* outPrettyClassName = nullptr, bool warnOnMissing = true); + static bool FindClass(const AZ::BehaviorMethod*& outMethod, const AZ::BehaviorClass*& outClass, AZStd::string_view className, AZStd::string_view methodName, PropertyStatus propertyStatus = PropertyStatus::None, AZStd::string* outPrettyClassName = nullptr, bool warnOnMissing = true); static bool FindEBus(const AZ::BehaviorEBus*& outEBus, AZStd::string_view ebusName, bool warnOnMissing = true); static bool FindExplicitOverload(const AZ::BehaviorMethod*& outMethod, const AZ::BehaviorClass*& outClass, AZStd::string_view className, AZStd::string_view methodName, AZStd::string* outPrettyClassName = nullptr); static AZStd::string FindExposedMethodName(const AZ::BehaviorMethod& method, const AZ::BehaviorClass* behaviorClass); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/NodeUtils.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/NodeUtils.cpp index 06ad79c21d..c6849f0b12 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/NodeUtils.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/NodeUtils.cpp @@ -58,7 +58,7 @@ namespace ScriptCanvas } else { - return ConstructMethodNodeIdentifier(methodNode->GetRawMethodClassName(), methodNode->GetName()); + return ConstructMethodNodeIdentifier(methodNode->GetRawMethodClassName(), methodNode->GetName(), methodNode->GetPropertyStatus()); } } else if (auto ebusNode = azrtti_cast(scriptCanvasNode)) @@ -158,13 +158,14 @@ namespace ScriptCanvas return resultHash; } - NodeTypeIdentifier NodeUtils::ConstructMethodNodeIdentifier(AZStd::string_view methodClass, AZStd::string_view methodName) + NodeTypeIdentifier NodeUtils::ConstructMethodNodeIdentifier(AZStd::string_view methodClass, AZStd::string_view methodName, ScriptCanvas::PropertyStatus propertyStatus) { NodeTypeIdentifier resultHash = 0; AZStd::hash_combine(resultHash, AZStd::hash()(azrtti_typeid())); AZStd::hash_combine(resultHash, AZStd::hash()(methodClass)); AZStd::hash_combine(resultHash, AZStd::hash()(methodName)); + AZStd::hash_combine(resultHash, AZStd::hash()(static_cast(propertyStatus))); return resultHash; } @@ -253,7 +254,7 @@ namespace ScriptCanvas if (auto* method = azrtti_cast(node)) { ScriptCanvas::NamespacePath emptyNamespaces; - method->InitializeBehaviorMethod(emptyNamespaces, config.m_className, config.m_methodName); + method->InitializeBehaviorMethod(emptyNamespaces, config.m_className, config.m_methodName, config.m_propertyStatus); } } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/NodeUtils.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/NodeUtils.h index 0f5b0f495d..ad8356c42e 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/NodeUtils.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Utils/NodeUtils.h @@ -38,7 +38,7 @@ namespace ScriptCanvas static NodeTypeIdentifier ConstructCustomNodeIdentifier(const AZ::Uuid& nodeId); - static NodeTypeIdentifier ConstructMethodNodeIdentifier(AZStd::string_view methodClass, AZStd::string_view methodName); + static NodeTypeIdentifier ConstructMethodNodeIdentifier(AZStd::string_view methodClass, AZStd::string_view methodName, ScriptCanvas::PropertyStatus propertyStatus); static NodeTypeIdentifier ConstructGlobalMethodNodeIdentifier(AZStd::string_view methodName); static NodeTypeIdentifier ConstructMethodOverloadedNodeIdentifier(AZStd::string_view methodName); diff --git a/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_UseRawBehaviorProperties.scriptcanvas b/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_UseRawBehaviorProperties.scriptcanvas new file mode 100644 index 0000000000..57e57116e0 --- /dev/null +++ b/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_UseRawBehaviorProperties.scriptcanvas @@ -0,0 +1,2618 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestFixture.h b/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestFixture.h index 7f026edf58..766353c4c5 100644 --- a/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestFixture.h +++ b/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestFixture.h @@ -21,23 +21,24 @@ #include #include #include +#include #include #include #include +#include +#include +#include #include #include #include #include -#include #include "EntityRefTests.h" #include "ScriptCanvasTestApplication.h" #include "ScriptCanvasTestBus.h" #include "ScriptCanvasTestNodes.h" #include "ScriptCanvasTestUtilities.h" -#include -#include #define SC_EXPECT_DOUBLE_EQ(candidate, reference) EXPECT_NEAR(candidate, reference, 0.001) #define SC_EXPECT_FLOAT_EQ(candidate, reference) EXPECT_NEAR(candidate, reference, 0.001f) @@ -112,6 +113,9 @@ namespace ScriptCanvasTests ScriptCanvasTesting::Reflect(m_serializeContext); ScriptCanvasTesting::Reflect(m_behaviorContext); + ScriptCanvasTestingNodes::BehaviorContextObjectTest::Reflect(m_serializeContext); + ScriptCanvasTestingNodes::BehaviorContextObjectTest::Reflect(m_behaviorContext); + ::Nodes::InputMethodSharedDataSlotExampleNode::Reflect(m_serializeContext); ::Nodes::InputMethodSharedDataSlotExampleNode::Reflect(m_behaviorContext); ::Nodes::BranchMethodSharedDataSlotExampleNode::Reflect(m_serializeContext); diff --git a/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestUtilities.cpp b/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestUtilities.cpp index 9fc023601e..bd31edad09 100644 --- a/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestUtilities.cpp +++ b/Gems/ScriptCanvasTesting/Code/Source/Framework/ScriptCanvasTestUtilities.cpp @@ -496,7 +496,7 @@ namespace ScriptCanvasTests ScriptCanvas::Nodes::Core::Method* methodNode(nullptr); SystemRequestBus::BroadcastResult(methodNode, &SystemRequests::GetNode, methodNodeID); EXPECT_TRUE(methodNode != nullptr); - methodNode->InitializeBehaviorMethod(emptyNamespaces, className, methodName); + methodNode->InitializeBehaviorMethod(emptyNamespaces, className, methodName, ScriptCanvas::PropertyStatus::None); return methodNodeID; } diff --git a/Gems/ScriptCanvasTesting/Code/Source/Nodes/BehaviorContextObjectTestNode.h b/Gems/ScriptCanvasTesting/Code/Source/Nodes/BehaviorContextObjectTestNode.h index 2460d37c02..7fa3896ff1 100644 --- a/Gems/ScriptCanvasTesting/Code/Source/Nodes/BehaviorContextObjectTestNode.h +++ b/Gems/ScriptCanvasTesting/Code/Source/Nodes/BehaviorContextObjectTestNode.h @@ -30,10 +30,10 @@ namespace ScriptCanvasTestingNodes { serializeContext->Class() ->Version(0) - ->Field("StringName", &BehaviorContextObjectTest::m_string) + ->Field("String", &BehaviorContextObjectTest::m_string) + ->Field("Name", &BehaviorContextObjectTest::m_name) ; - - + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { editContext->Class("Behavior Context Object Test", "An Object that lives within Behavior Context exclusively for testing") @@ -52,6 +52,7 @@ namespace ScriptCanvasTestingNodes ->Attribute(AZ::Script::Attributes::Category, "Tests/Behavior Context") ->Method("SetString", &BehaviorContextObjectTest::SetString) ->Method("GetString", &BehaviorContextObjectTest::GetString) + ->Property("Name", BehaviorValueProperty(&BehaviorContextObjectTest::m_name)) ; } } @@ -73,7 +74,7 @@ namespace ScriptCanvasTestingNodes } private: - + AZStd::string m_name; AZStd::string m_string; }; diff --git a/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_RuntimeInterpreted.cpp b/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_RuntimeInterpreted.cpp index a46bb3323b..1633b0fc51 100644 --- a/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_RuntimeInterpreted.cpp +++ b/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_RuntimeInterpreted.cpp @@ -90,6 +90,11 @@ public: } }; +TEST_F(ScriptCanvasTestFixture, UseRawBehaviorProperties) +{ + RunUnitTestGraph("LY_SC_UnitTest_UseRawBehaviorProperties"); +} + TEST_F(ScriptCanvasTestFixture, StringSanitization) { RunUnitTestGraph("LY_SC_UnitTest_StringSanitization"); diff --git a/Gems/SurfaceData/Code/Source/Components/SurfaceDataColliderComponent.cpp b/Gems/SurfaceData/Code/Source/Components/SurfaceDataColliderComponent.cpp index 45f1970ac2..f994283fbf 100644 --- a/Gems/SurfaceData/Code/Source/Components/SurfaceDataColliderComponent.cpp +++ b/Gems/SurfaceData/Code/Source/Components/SurfaceDataColliderComponent.cpp @@ -19,8 +19,8 @@ #include #include -#include #include +#include #include #include @@ -221,7 +221,7 @@ namespace SurfaceData } AzPhysics::SceneQueryHit result; - Physics::WorldBodyRequestBus::EventResult(result, GetEntityId(), &Physics::WorldBodyRequestBus::Events::RayCast, request); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(result, GetEntityId(), &AzPhysics::SimulatedBodyComponentRequestsBus::Events::RayCast, request); if (result) { @@ -319,7 +319,7 @@ namespace SurfaceData colliderValidBeforeUpdate = m_colliderBounds.IsValid(); m_colliderBounds = AZ::Aabb::CreateNull(); - Physics::WorldBodyRequestBus::EventResult(m_colliderBounds, GetEntityId(), &Physics::WorldBodyRequestBus::Events::GetAabb); + AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(m_colliderBounds, GetEntityId(), &AzPhysics::SimulatedBodyComponentRequestsBus::Events::GetAabb); colliderValidAfterUpdate = m_colliderBounds.IsValid(); } diff --git a/Gems/SurfaceData/Code/Tests/SurfaceDataColliderComponentTest.cpp b/Gems/SurfaceData/Code/Tests/SurfaceDataColliderComponentTest.cpp index 2d232854f3..db3bb1881d 100644 --- a/Gems/SurfaceData/Code/Tests/SurfaceDataColliderComponentTest.cpp +++ b/Gems/SurfaceData/Code/Tests/SurfaceDataColliderComponentTest.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include namespace UnitTest { @@ -45,12 +45,12 @@ namespace UnitTest }; class MockPhysicsWorldBusProvider - : public Physics::WorldBodyRequestBus::Handler + : public AzPhysics::SimulatedBodyComponentRequestsBus::Handler { public: MockPhysicsWorldBusProvider(const AZ::EntityId& id, AZ::Vector3 inPosition, bool setHitResult, const SurfaceData::SurfacePoint& hitResult) { - Physics::WorldBodyRequestBus::Handler::BusConnect(id); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(id); // Whether or not the test should return a successful hit, we still want to create a valid // AABB so that the SurfaceData component registers itself as a provider. @@ -73,14 +73,15 @@ namespace UnitTest virtual ~MockPhysicsWorldBusProvider() { - Physics::WorldBodyRequestBus::Handler::BusDisconnect(); + AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusDisconnect(); } // Minimal mocks needed to mock out this ebus void EnablePhysics() override {} void DisablePhysics() override {} bool IsPhysicsEnabled() const override { return true; } - AzPhysics::SimulatedBody* GetWorldBody() override { return nullptr; } + AzPhysics::SimulatedBody* GetSimulatedBody() override { return nullptr; } + AzPhysics::SimulatedBodyHandle GetSimulatedBodyHandle() const override { return AzPhysics::InvalidSimulatedBodyHandle; } // Functional mocks to mock out the data needed by the component AZ::Aabb GetAabb() const override { return m_aabb; } diff --git a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake index 4056242a47..620995a85b 100644 --- a/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake +++ b/cmake/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake @@ -14,7 +14,7 @@ ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform TARG ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) -ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev8-multiplatform TARGETS assimplib PACKAGE_HASH 21dce424eccf5a2626ff0841e72092fa4ff9407a64b851e5eed9895926ce309d) +ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev7-multiplatform TARGETS assimplib PACKAGE_HASH def855c89d8210db3040f1cb6ec837141ab9b8e74c158eae7c03d50160fcf30b) ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index e564d64fc3..8636715c39 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -14,7 +14,7 @@ ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) -ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev8-multiplatform TARGETS assimplib PACKAGE_HASH 21dce424eccf5a2626ff0841e72092fa4ff9407a64b851e5eed9895926ce309d) +ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev7-multiplatform TARGETS assimplib PACKAGE_HASH def855c89d8210db3040f1cb6ec837141ab9b8e74c158eae7c03d50160fcf30b) ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 939948d501..bdc93d9a0e 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -14,7 +14,7 @@ ly_associate_package(PACKAGE_NAME zlib-1.2.8-rev2-multiplatform ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348) ly_associate_package(PACKAGE_NAME hdf5-1.0.11-rev2-multiplatform TARGETS hdf5 PACKAGE_HASH 11d5e04df8a93f8c52a5684a4cacbf0d9003056360983ce34f8d7b601082c6bd) ly_associate_package(PACKAGE_NAME alembic-1.7.11-rev3-multiplatform TARGETS alembic PACKAGE_HASH ba7a7d4943dd752f5a662374f6c48b93493df1d8e2c5f6a8d101f3b50700dd25) -ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev8-multiplatform TARGETS assimplib PACKAGE_HASH 21dce424eccf5a2626ff0841e72092fa4ff9407a64b851e5eed9895926ce309d) +ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev7-multiplatform TARGETS assimplib PACKAGE_HASH def855c89d8210db3040f1cb6ec837141ab9b8e74c158eae7c03d50160fcf30b) ly_associate_package(PACKAGE_NAME squish-ccr-20150601-rev3-multiplatform TARGETS squish-ccr PACKAGE_HASH c878c6c0c705e78403c397d03f5aa7bc87e5978298710e14d09c9daf951a83b3) ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326) ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf) diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index 25fddb90d0..4f6565edc7 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -13,7 +13,13 @@ if(NOT PAL_TRAIT_BUILD_CPACK_SUPPORTED) return() endif() -set(CPACK_GENERATOR "ZIP") +ly_get_absolute_pal_filename(pal_dir ${CMAKE_SOURCE_DIR}/cmake/Platform/${PAL_HOST_PLATFORM_NAME}) +include(${pal_dir}/Packaging_${PAL_HOST_PLATFORM_NAME_LOWERCASE}.cmake) + +# if we get here and the generator hasn't been set, then a non fatal error occurred disabling packaging support +if(NOT CPACK_GENERATOR) + return() +endif() set(CPACK_PACKAGE_VENDOR "${PROJECT_NAME}") set(CPACK_PACKAGE_VERSION "${LY_VERSION_STRING}") @@ -27,6 +33,8 @@ set(DEFAULT_LICENSE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt") set(CPACK_RESOURCE_FILE_LICENSE ${DEFAULT_LICENSE_FILE}) +set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_VENDOR}/${CPACK_PACKAGE_VERSION}") + # IMPORTANT: required to be included AFTER setting all property overrides include(CPack REQUIRED) diff --git a/cmake/Platform/Windows/PackagingTemplate.wxs.in b/cmake/Platform/Windows/PackagingTemplate.wxs.in new file mode 100644 index 0000000000..3e5db03ec2 --- /dev/null +++ b/cmake/Platform/Windows/PackagingTemplate.wxs.in @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + ProductIcon.ico + + + + + + + + + + + + + + + + + + + diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake new file mode 100644 index 0000000000..8aa6f2386d --- /dev/null +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -0,0 +1,92 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +set(LY_WIX_PATH "" CACHE PATH "Path to the WiX install path") + +if(LY_WIX_PATH) + file(TO_CMAKE_PATH ${LY_QTIFW_PATH} CPACK_WIX_ROOT) +elseif(DEFINED ENV{WIX}) + file(TO_CMAKE_PATH $ENV{WIX} CPACK_WIX_ROOT) +endif() + +if(CPACK_WIX_ROOT) + if(NOT EXISTS ${CPACK_WIX_ROOT}) + message(FATAL_ERROR "Invalid path supplied for LY_WIX_PATH argument or WIX environment variable") + endif() +else() + # early out as no path to WiX has been supplied effectively disabling support + return() +endif() + +set(CPACK_GENERATOR "WIX") + +# CPack will generate the WiX product/upgrade GUIDs further down the chain if they weren't supplied +# however, they are unique for each run. instead, let's do the auto generation here and add it to +# the cache for run persistence. an additional cache file will be used to store the information on +# the original generation so we still have the ability to detect if they are still being used. +set(_guid_cache_file "${CMAKE_BINARY_DIR}/installer/wix_guid_cache.cmake") +if(NOT EXISTS ${_guid_cache_file}) + set(_wix_guid_namespace "6D43F57A-2917-4AD9-B758-1F13CDB08593") + + # based the ISO-8601 standard (YYYY-MM-DDTHH-mm-ssTZD) e.g., 20210506145533 + string(TIMESTAMP _guid_gen_timestamp "%Y%m%d%H%M%S") + + file(WRITE ${_guid_cache_file} "set(_wix_guid_gen_timestamp ${_guid_gen_timestamp})\n") + + string(UUID _default_product_guid + NAMESPACE ${_wix_guid_namespace} + NAME "ProductID_${_guid_gen_timestamp}" + TYPE SHA1 + UPPER + ) + file(APPEND ${_guid_cache_file} "set(_wix_default_product_guid ${_default_product_guid})\n") + + string(UUID _default_upgrade_guid + NAMESPACE ${_wix_guid_namespace} + NAME "UpgradeCode_${_guid_gen_timestamp}" + TYPE SHA1 + UPPER + ) + file(APPEND ${_guid_cache_file} "set(_wix_default_upgrade_guid ${_default_upgrade_guid})\n") +endif() +include(${_guid_cache_file}) + +set(LY_WIX_PRODUCT_GUID "${_wix_default_product_guid}" CACHE STRING "GUID for the Product ID field. Format: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") +set(LY_WIX_UPGRADE_GUID "${_wix_default_upgrade_guid}" CACHE STRING "GUID for the Upgrade Code field. Format: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") + +set(_uses_default_product_guid FALSE) +if(NOT LY_WIX_PRODUCT_GUID OR LY_WIX_PRODUCT_GUID STREQUAL ${_wix_default_product_guid}) + set(_uses_default_product_guid TRUE) + set(LY_WIX_PRODUCT_GUID ${_wix_default_product_guid}) +endif() + +set(_uses_default_upgrade_guid FALSE) +if(NOT LY_WIX_UPGRADE_GUID OR LY_WIX_UPGRADE_GUID STREQUAL ${_wix_default_upgrade_guid}) + set(_uses_default_upgrade_guid TRUE) + set(LY_WIX_UPGRADE_GUID ${_wix_default_upgrade_guid}) +endif() + +if(_uses_default_product_guid OR _uses_default_upgrade_guid) + message(STATUS "One or both WiX GUIDs were auto generated. It is recommended you supply your own GUIDs through LY_WIX_PRODUCT_GUID and LY_WIX_UPGRADE_GUID.") + + if(_uses_default_product_guid) + message(STATUS "-> Default LY_WIX_PRODUCT_GUID = ${LY_WIX_PRODUCT_GUID}") + endif() + + if(_uses_default_upgrade_guid) + message(STATUS "-> Default LY_WIX_UPGRADE_GUID = ${LY_WIX_UPGRADE_GUID}") + endif() +endif() + +set(CPACK_WIX_PRODUCT_GUID ${LY_WIX_PRODUCT_GUID}) +set(CPACK_WIX_UPGRADE_GUID ${LY_WIX_UPGRADE_GUID}) + +set(CPACK_WIX_TEMPLATE "${CMAKE_SOURCE_DIR}/cmake/Platform/Windows/PackagingTemplate.wxs.in") diff --git a/cmake/Platform/Windows/platform_windows_files.cmake b/cmake/Platform/Windows/platform_windows_files.cmake index bf9cb05d17..2fc869b43e 100644 --- a/cmake/Platform/Windows/platform_windows_files.cmake +++ b/cmake/Platform/Windows/platform_windows_files.cmake @@ -23,4 +23,6 @@ set(FILES PAL_windows.cmake PALDetection_windows.cmake Install_windows.cmake + Packaging_windows.cmake + PackagingTemplate.wxs.in ) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index bc770e42f7..f85b0b5ef4 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -549,15 +549,17 @@ finally { message:"${currentBuild.currentResult}:${BUILD_URL}:${env.RECREATE_VOLUME}:${env.CLEAN_OUTPUT_DIRECTORY}:${env.CLEAN_ASSETS}" ) } - step([ - $class: 'Mailer', - notifyEveryUnstableBuild: true, - sendToIndividuals: true, - recipients: emailextrecipients([ - [$class: 'CulpritsRecipientProvider'], - [$class: 'RequesterRecipientProvider'] + node('controller') { + emailRecipients = [[$class: 'RequesterRecipientProvider']] + if (env.WATCHED_BRANCHES.tokenize(',').contains(branchName)) { + emailRecipients.add([$class: 'CulpritsRecipientProvider']) + } + step([ + $class: 'Mailer', + notifyEveryUnstableBuild: true, + recipients: emailextrecipients(emailRecipients) ]) - ]) + } } catch(Exception e) { } } diff --git a/scripts/build/Platform/Android/build_config.json b/scripts/build/Platform/Android/build_config.json index 07dc363296..699ccdf20a 100644 --- a/scripts/build/Platform/Android/build_config.json +++ b/scripts/build/Platform/Android/build_config.json @@ -27,7 +27,8 @@ }, "debug": { "TAGS":[ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND":"../Windows/build_ninja_windows.cmd", @@ -67,7 +68,8 @@ }, "profile_nounity": { "TAGS":[ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND":"../Windows/build_ninja_windows.cmd", @@ -83,7 +85,9 @@ "asset_profile": { "TAGS":[ "default", - "weekly-build-metrics" + "weekly-build-metrics", + "nightly-incremental", + "nightly-clean" ], "COMMAND":"../Windows/build_asset_windows.cmd", "PARAMETERS": { @@ -98,21 +102,10 @@ "ASSET_PROCESSOR_PLATFORMS":"es3" } }, - "asset_clean_profile": { - "TAGS":[ - "nightly" - ], - "PIPELINE_ENV": { - "CLEAN_ASSETS": "1" - }, - "steps": [ - "clean", - "asset_profile" - ] - }, "release": { "TAGS":[ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND":"../Windows/build_ninja_windows.cmd", @@ -127,7 +120,8 @@ }, "monolithic_release": { "TAGS":[ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND":"../Windows/build_ninja_windows.cmd", diff --git a/scripts/build/Platform/Android/pipeline.json b/scripts/build/Platform/Android/pipeline.json index a4a2700af0..ed10e7022d 100644 --- a/scripts/build/Platform/Android/pipeline.json +++ b/scripts/build/Platform/Android/pipeline.json @@ -13,6 +13,9 @@ }, "packaging": { "CLEAN_WORKSPACE": true + }, + "nightly-clean": { + "CLEAN_WORKSPACE": true } } } \ No newline at end of file diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index 426bf1d7ce..4ae4c4ec0b 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -29,7 +29,8 @@ }, "debug": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_linux.sh", @@ -43,9 +44,10 @@ }, "profile": { "TAGS": [ - "nightly", - "daily-pipeline-metrics", - "weekly-build-metrics" + "nightly-incremental", + "nightly-clean", + "daily-pipeline-metrics", + "weekly-build-metrics" ], "COMMAND": "build_linux.sh", "PARAMETERS": { @@ -98,7 +100,9 @@ }, "asset_profile": { "TAGS": [ - "weekly-build-metrics" + "weekly-build-metrics", + "nightly-incremental", + "nightly-clean" ], "COMMAND": "build_asset_linux.sh", "PARAMETERS": { @@ -126,21 +130,10 @@ "ASSET_PROCESSOR_PLATFORMS": "pc,server" } }, - "asset_clean_profile": { - "TAGS": [ - "nightly" - ], - "PIPELINE_ENV": { - "CLEAN_ASSETS": "1" - }, - "steps": [ - "clean", - "asset_profile" - ] - }, "periodic_test_profile": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_test_linux.sh", @@ -155,7 +148,8 @@ }, "benchmark_test_profile": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_test_linux.sh", @@ -170,7 +164,8 @@ }, "release": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_linux.sh", @@ -184,7 +179,8 @@ }, "monolithic_release": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_linux.sh", diff --git a/scripts/build/Platform/Linux/pipeline.json b/scripts/build/Platform/Linux/pipeline.json index ec06dff5bf..d964a693ce 100644 --- a/scripts/build/Platform/Linux/pipeline.json +++ b/scripts/build/Platform/Linux/pipeline.json @@ -12,6 +12,9 @@ }, "packaging": { "CLEAN_WORKSPACE": true + }, + "nightly-clean": { + "CLEAN_WORKSPACE": true } } } \ No newline at end of file diff --git a/scripts/build/Platform/Mac/build_config.json b/scripts/build/Platform/Mac/build_config.json index f816117331..1e6ca79d8e 100644 --- a/scripts/build/Platform/Mac/build_config.json +++ b/scripts/build/Platform/Mac/build_config.json @@ -9,7 +9,8 @@ }, "profile_pipe": { "TAGS": [ - "nightly" + "nightly-incremental", + "nightly-clean" ], "steps": [ "profile", @@ -28,7 +29,8 @@ }, "debug": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_mac.sh", @@ -56,7 +58,8 @@ }, "profile_nounity": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_mac.sh", @@ -70,7 +73,9 @@ }, "asset_profile": { "TAGS": [ - "weekly-build-metrics" + "weekly-build-metrics", + "nightly-incremental", + "nightly-clean" ], "COMMAND": "build_asset_mac.sh", "PARAMETERS": { @@ -84,21 +89,10 @@ "ASSET_PROCESSOR_PLATFORMS": "osx_gl" } }, - "asset_clean_profile": { - "TAGS": [ - "nightly" - ], - "PIPELINE_ENV": { - "CLEAN_ASSETS": "1" - }, - "steps": [ - "clean", - "asset_profile" - ] - }, "periodic_test_profile": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_test_mac.sh", @@ -113,7 +107,8 @@ }, "benchmark_test_profile": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_test_mac.sh", @@ -128,7 +123,8 @@ }, "release": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_mac.sh", @@ -142,7 +138,8 @@ }, "monolithic_release": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_mac.sh", diff --git a/scripts/build/Platform/Mac/pipeline.json b/scripts/build/Platform/Mac/pipeline.json index bb94f33d70..58f62b421d 100644 --- a/scripts/build/Platform/Mac/pipeline.json +++ b/scripts/build/Platform/Mac/pipeline.json @@ -12,6 +12,9 @@ }, "packaging": { "CLEAN_WORKSPACE": true + }, + "nightly-clean": { + "CLEAN_WORKSPACE": true } } } \ No newline at end of file diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index bd10c9e59a..263539cd4a 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -17,7 +17,8 @@ }, "debug_vs2019_pipe": { "TAGS": [ - "nightly" + "nightly-incremental", + "nightly-clean" ], "steps": [ "debug_vs2019", @@ -125,7 +126,8 @@ }, "profile_vs2019_nounity": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_windows.cmd", @@ -157,10 +159,11 @@ }, "test_gpu_profile_vs2019": { "TAGS":[ - "nightly" + "nightly-incremental", + "nightly-clean" ], "PIPELINE_ENV":{ - "NODE_LABEL":"windows-gpu" + "NODE_LABEL":"windows-gpu" }, "COMMAND": "build_test_windows.cmd", "PARAMETERS": { @@ -176,7 +179,9 @@ }, "asset_profile_vs2019": { "TAGS": [ - "weekly-build-metrics" + "weekly-build-metrics", + "nightly-incremental", + "nightly-clean" ], "COMMAND": "build_asset_windows.cmd", "PARAMETERS": { @@ -191,21 +196,10 @@ "ASSET_PROCESSOR_PLATFORMS": "pc,server" } }, - "asset_clean_profile_vs2019": { - "TAGS": [ - "nightly" - ], - "PIPELINE_ENV": { - "CLEAN_ASSETS": "1" - }, - "steps": [ - "clean", - "asset_profile_vs2019" - ] - }, "periodic_test_profile_vs2019": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_test_windows.cmd", @@ -222,7 +216,8 @@ }, "sandbox_test_profile_vs2019": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "PIPELINE_ENV": { @@ -242,7 +237,8 @@ }, "benchmark_test_profile_vs2019": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_test_windows.cmd", @@ -259,7 +255,8 @@ }, "release_vs2019": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_windows.cmd", @@ -274,7 +271,8 @@ }, "monolithic_release_vs2019": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "build_windows.cmd", @@ -289,7 +287,8 @@ }, "install_profile_vs2019": { "TAGS": [ - "nightly" + "nightly-incremental", + "nightly-clean" ], "COMMAND": "build_windows.cmd", "PARAMETERS": { diff --git a/scripts/build/Platform/Windows/pipeline.json b/scripts/build/Platform/Windows/pipeline.json index 380b8c1a48..5f10ccc7ae 100644 --- a/scripts/build/Platform/Windows/pipeline.json +++ b/scripts/build/Platform/Windows/pipeline.json @@ -12,6 +12,9 @@ }, "packaging": { "CLEAN_WORKSPACE": true + }, + "nightly-clean": { + "CLEAN_WORKSPACE": true } } } \ No newline at end of file diff --git a/scripts/build/Platform/iOS/build_config.json b/scripts/build/Platform/iOS/build_config.json index 921262e267..75b5e7b10d 100644 --- a/scripts/build/Platform/iOS/build_config.json +++ b/scripts/build/Platform/iOS/build_config.json @@ -19,7 +19,8 @@ }, "debug": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "../Mac/build_mac.sh", @@ -34,7 +35,8 @@ }, "profile": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "daily-pipeline-metrics", "weekly-build-metrics" ], @@ -50,7 +52,8 @@ }, "profile_nounity": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "../Mac/build_mac.sh", @@ -65,7 +68,8 @@ }, "asset_profile": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "../Mac/build_asset_mac.sh", @@ -80,21 +84,10 @@ "ASSET_PROCESSOR_PLATFORMS": "ios" } }, - "asset_clean_profile": { - "TAGS": [ - "nightly" - ], - "PIPELINE_ENV": { - "CLEAN_ASSETS": "true" - }, - "steps": [ - "clean", - "asset_profile" - ] - }, "release": { "TAGS": [ - "nightly", + "nightly-incremental", + "nightly-clean", "weekly-build-metrics" ], "COMMAND": "../Mac/build_mac.sh", diff --git a/scripts/build/Platform/iOS/pipeline.json b/scripts/build/Platform/iOS/pipeline.json index bb94f33d70..58f62b421d 100644 --- a/scripts/build/Platform/iOS/pipeline.json +++ b/scripts/build/Platform/iOS/pipeline.json @@ -12,6 +12,9 @@ }, "packaging": { "CLEAN_WORKSPACE": true + }, + "nightly-clean": { + "CLEAN_WORKSPACE": true } } } \ No newline at end of file