diff --git a/AutomatedTesting/Gem/Code/Platform/Mac/tool_dependencies.cmake b/AutomatedTesting/Gem/Code/Platform/Mac/tool_dependencies.cmake index ee7be9ac6d..2e9d450ab8 100644 --- a/AutomatedTesting/Gem/Code/Platform/Mac/tool_dependencies.cmake +++ b/AutomatedTesting/Gem/Code/Platform/Mac/tool_dependencies.cmake @@ -14,4 +14,6 @@ set(GEM_DEPENDENCIES Gem::Atom_RHI_Null.Builders Gem::Atom_RHI_Metal.Private Gem::Atom_RHI_Metal.Builders + Gem::Atom_RHI_Vulkan.Builders + Gem::Atom_RHI_DX12.Builders ) \ 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 42fe9ac4a1..3124f1048a 100644 --- a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt @@ -51,7 +51,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) TEST_SUITE sandbox TEST_SERIAL PATH ${CMAKE_CURRENT_LIST_DIR}/physics/TestSuite_Sandbox.py - TIMEOUT 3600 + TIMEOUT 1500 RUNTIME_DEPENDENCIES Legacy::Editor AZ::AssetProcessor @@ -68,7 +68,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) TEST_SUITE periodic TEST_SERIAL PATH ${CMAKE_CURRENT_LIST_DIR}/scripting/TestSuite_Active.py - TIMEOUT 1500 + TIMEOUT 3000 RUNTIME_DEPENDENCIES Legacy::Editor AZ::AssetProcessor @@ -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/EditorPythonTestTools/editor_python_test_tools/utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py index a9f6d0aa02..c7f1aba031 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py @@ -265,6 +265,7 @@ class Tracer: self.warnings = [] self.errors = [] self.asserts = [] + self.prints = [] self.has_warnings = False self.has_errors = False self.has_asserts = False @@ -310,6 +311,11 @@ class Tracer: def __repr__(self): return f"[Assert: {self.message}]" + + class PrintInfo: + def __init__(self, args): + self.window = args[0] + self.message = args[1] def _on_warning(self, args): warningInfo = Tracer.WarningInfo(args) @@ -331,13 +337,19 @@ class Tracer: Report.info("Tracer caught Assert: %s:%i[%s] \"%s\"" % (assertInfo.filename, assertInfo.line, assertInfo.function, assertInfo.message)) self.has_asserts = True return False - + + def _on_printf(self, args): + printInfo = Tracer.PrintInfo(args) + self.prints.append(printInfo) + return False + def __enter__(self): self.handler = azlmbr.debug.TraceMessageBusHandler() self.handler.connect(None) self.handler.add_callback("OnPreAssert", self._on_assert) self.handler.add_callback("OnPreWarning", self._on_warning) self.handler.add_callback("OnPreError", self._on_error) + self.handler.add_callback("OnPrintf", self._on_printf) return self def __exit__(self, type, value, traceback): 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/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/C100000_RigidBody_EnablingGravityWorksPoC.py b/AutomatedTesting/Gem/PythonTests/physics/C100000_RigidBody_EnablingGravityWorksPoC.py index e0277180ae..1284c234bd 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C100000_RigidBody_EnablingGravityWorksPoC.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C100000_RigidBody_EnablingGravityWorksPoC.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C100000 # Test Case Title : Check that Gravity works -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/100000 + # fmt:off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C111111_RigidBody_EnablingGravityWorksUsingNotificationsPoC.py b/AutomatedTesting/Gem/PythonTests/physics/C111111_RigidBody_EnablingGravityWorksUsingNotificationsPoC.py index c7dd8debb9..725da5fb24 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C111111_RigidBody_EnablingGravityWorksUsingNotificationsPoC.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C111111_RigidBody_EnablingGravityWorksUsingNotificationsPoC.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C111111 # Test Case Title : Check that Gravity works -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/111111 + # fmt:off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C12712452_ScriptCanvas_CollisionEvents.py b/AutomatedTesting/Gem/PythonTests/physics/C12712452_ScriptCanvas_CollisionEvents.py index 216a75233f..380bb8a3c8 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C12712452_ScriptCanvas_CollisionEvents.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C12712452_ScriptCanvas_CollisionEvents.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test Case ID : C12712452 # Test Case Title : Verify ScriptCanvas Collision Events -# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/12712452 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C12712453_ScriptCanvas_MultipleRaycastNode.py b/AutomatedTesting/Gem/PythonTests/physics/C12712453_ScriptCanvas_MultipleRaycastNode.py index 2adf30e65e..b415688de7 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C12712453_ScriptCanvas_MultipleRaycastNode.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C12712453_ScriptCanvas_MultipleRaycastNode.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C12712453 # Test Case Title : Verify Raycast Multiple Node -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/12712453 + # fmt:off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C12712454_ScriptCanvas_OverlapNodeVerification.py b/AutomatedTesting/Gem/PythonTests/physics/C12712454_ScriptCanvas_OverlapNodeVerification.py index 94eed900ff..bcf980a710 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C12712454_ScriptCanvas_OverlapNodeVerification.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C12712454_ScriptCanvas_OverlapNodeVerification.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : 12712454 # Test Case Title : Verify overlap nodes in script canvas -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/12712454 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C12712455_ScriptCanvas_ShapeCastVerification.py b/AutomatedTesting/Gem/PythonTests/physics/C12712455_ScriptCanvas_ShapeCastVerification.py index 61f26ebecb..668e8083c3 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C12712455_ScriptCanvas_ShapeCastVerification.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C12712455_ScriptCanvas_ShapeCastVerification.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C12712455 # Test Case Title : Verify shape cast nodes in SC -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/12712455 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C12868578_ForceRegion_DirectionHasNoAffectOnMagnitude.py b/AutomatedTesting/Gem/PythonTests/physics/C12868578_ForceRegion_DirectionHasNoAffectOnMagnitude.py index 4745927e89..d2438110fe 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C12868578_ForceRegion_DirectionHasNoAffectOnMagnitude.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C12868578_ForceRegion_DirectionHasNoAffectOnMagnitude.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C12868578 # Test Case Title : Check that World space and local space force direction doesn't affect magnitude of force exerted -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/12868578 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C12868580_ForceRegion_SplineModifiedTransform.py b/AutomatedTesting/Gem/PythonTests/physics/C12868580_ForceRegion_SplineModifiedTransform.py index 937fdd85a6..6372104fcf 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C12868580_ForceRegion_SplineModifiedTransform.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C12868580_ForceRegion_SplineModifiedTransform.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C12868580 # Test Case Title : Check that spline follow force works if transform components of entity are altered -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/12868580 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C12905527_ForceRegion_MagnitudeDeviation.py b/AutomatedTesting/Gem/PythonTests/physics/C12905527_ForceRegion_MagnitudeDeviation.py index 0cb0a2f841..76578460d5 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C12905527_ForceRegion_MagnitudeDeviation.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C12905527_ForceRegion_MagnitudeDeviation.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C12905527 # Test Case Title : Check that deviation occurring in Force Magnitude due to Values in Force direction is not large -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/12905527 + # fmt: off class Tests(): diff --git a/AutomatedTesting/Gem/PythonTests/physics/C12905528_ForceRegion_WithNonTriggerCollider.py b/AutomatedTesting/Gem/PythonTests/physics/C12905528_ForceRegion_WithNonTriggerCollider.py index 42565333f7..38dc5488d2 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C12905528_ForceRegion_WithNonTriggerCollider.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C12905528_ForceRegion_WithNonTriggerCollider.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C12905528 Test Case Title : Check that user is warned if non-trigger collider component is used with force region -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/12905528 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C13351703_COM_NotIncludeTriggerShapes.py b/AutomatedTesting/Gem/PythonTests/physics/C13351703_COM_NotIncludeTriggerShapes.py index 828022ccf2..d5bd946d34 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C13351703_COM_NotIncludeTriggerShapes.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C13351703_COM_NotIncludeTriggerShapes.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C13351703 # Test Case Title : Check that Center of Mass calculations should not include trigger shapes -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/13351703 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C13352089_RigidBodies_MaxAngularVelocity.py b/AutomatedTesting/Gem/PythonTests/physics/C13352089_RigidBodies_MaxAngularVelocity.py index 77d93d9752..09b6956b06 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C13352089_RigidBodies_MaxAngularVelocity.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C13352089_RigidBodies_MaxAngularVelocity.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C13352089 # Test Case Title : Verify that maximum angular velocity interacts correctly with initial angular velocity -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/13352089 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C13508019_Terrain_TerrainTexturePainterWorks.py b/AutomatedTesting/Gem/PythonTests/physics/C13508019_Terrain_TerrainTexturePainterWorks.py index 1c580bfbfe..9c7a6e75e9 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C13508019_Terrain_TerrainTexturePainterWorks.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C13508019_Terrain_TerrainTexturePainterWorks.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C13508019 # Test Case Title : Verify terrain materials are updated after using terrain texture layer painter. -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/13508019 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C13895144_Ragdoll_ChangeLevel.py b/AutomatedTesting/Gem/PythonTests/physics/C13895144_Ragdoll_ChangeLevel.py index 61efe20dbd..66050565a4 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C13895144_Ragdoll_ChangeLevel.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C13895144_Ragdoll_ChangeLevel.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C13895144 # Test Case Title : Run a level with multiple ragdolls and then switch levels -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/13895144 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C14195074_ScriptCanvas_PostUpdateEvent.py b/AutomatedTesting/Gem/PythonTests/physics/C14195074_ScriptCanvas_PostUpdateEvent.py index 5668082f78..1d0e1d5ac8 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C14195074_ScriptCanvas_PostUpdateEvent.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C14195074_ScriptCanvas_PostUpdateEvent.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C14195074 # Test Case Title : Verify Postsimulate Events -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14195074 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C14654881_CharacterController_SwitchLevels.py b/AutomatedTesting/Gem/PythonTests/physics/C14654881_CharacterController_SwitchLevels.py index fd5653b801..8fc58b4765 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C14654881_CharacterController_SwitchLevels.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C14654881_CharacterController_SwitchLevels.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C14654881 # Test Case Title : Switching levels from a level containing a character controller component # should not lead to a crash -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14654881 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C14654882_Ragdoll_ragdollAPTest.py b/AutomatedTesting/Gem/PythonTests/physics/C14654882_Ragdoll_ragdollAPTest.py index 03c18a5f1a..33077f2607 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C14654882_Ragdoll_ragdollAPTest.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C14654882_Ragdoll_ragdollAPTest.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ Test case ID : C14654882 Test Case Title : Loading level with old PhysX Ragdoll component serialization should not produce asset processor errors -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14654882 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C14861498_ConfirmError_NoPxMesh.py b/AutomatedTesting/Gem/PythonTests/physics/C14861498_ConfirmError_NoPxMesh.py index e52bdb7574..dca831456d 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C14861498_ConfirmError_NoPxMesh.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C14861498_ConfirmError_NoPxMesh.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C14861498 # Test Case Title : Confirm that when a PhysXCollider has no physics asset, the physics asset collider \ # shape throw an error -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14861498 + # fmt:off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C14861500_DefaultSetting_ColliderShape.py b/AutomatedTesting/Gem/PythonTests/physics/C14861500_DefaultSetting_ColliderShape.py index 7a65448dcd..c6a275d04d 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C14861500_DefaultSetting_ColliderShape.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C14861500_DefaultSetting_ColliderShape.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C14861500 Test Case Title : Verify Default shape is Physics Asset -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14861500 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C14861501_PhysXCollider_RenderMeshAutoAssigned.py b/AutomatedTesting/Gem/PythonTests/physics/C14861501_PhysXCollider_RenderMeshAutoAssigned.py index 904b5b0189..bc7b1b5e00 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C14861501_PhysXCollider_RenderMeshAutoAssigned.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C14861501_PhysXCollider_RenderMeshAutoAssigned.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C14861501 Test Case Title : Verify PxMesh is auto-assigned when Collider component is added after Rendering Mesh component -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14861501 + """ @@ -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/C14861502_PhysXCollider_AssetAutoAssigned.py b/AutomatedTesting/Gem/PythonTests/physics/C14861502_PhysXCollider_AssetAutoAssigned.py index 075c3f5b61..64820f7daf 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C14861502_PhysXCollider_AssetAutoAssigned.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C14861502_PhysXCollider_AssetAutoAssigned.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C14861502 Test Case Title : Verify PxMesh is auto-assigned in collider when Mesh is assigned in Rendering Mesh component -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14861502 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C14861504_RenderMeshAsset_WithNoPxAsset.py b/AutomatedTesting/Gem/PythonTests/physics/C14861504_RenderMeshAsset_WithNoPxAsset.py index bbaabf67f6..766a359397 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C14861504_RenderMeshAsset_WithNoPxAsset.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C14861504_RenderMeshAsset_WithNoPxAsset.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C14861504 Test Case Title : Verify if Rendering Mesh does not have a PhysX Collision Mesh fbx, then PxMesh is not auto-assigned -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14861504 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C14902097_ScriptCanvas_PreUpdateEvent.py b/AutomatedTesting/Gem/PythonTests/physics/C14902097_ScriptCanvas_PreUpdateEvent.py index 45557098f7..327f40ec38 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C14902097_ScriptCanvas_PreUpdateEvent.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C14902097_ScriptCanvas_PreUpdateEvent.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C14902097 # Test Case Title : Verify Presimulate Events -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14902097 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C14902098_ScriptCanvas_PostPhysicsUpdate.py b/AutomatedTesting/Gem/PythonTests/physics/C14902098_ScriptCanvas_PostPhysicsUpdate.py index 19caaab4fe..f7c8be8804 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C14902098_ScriptCanvas_PostPhysicsUpdate.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C14902098_ScriptCanvas_PostPhysicsUpdate.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C14902098 # Test Case Title : Check that force region simulation with Postsimulate works independently from rendering tick -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14902098 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C14976307_Gravity_SetGravityWorks.py b/AutomatedTesting/Gem/PythonTests/physics/C14976307_Gravity_SetGravityWorks.py index 342ac6bdbd..73137ac497 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C14976307_Gravity_SetGravityWorks.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C14976307_Gravity_SetGravityWorks.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C14976307 # Test Case Title : Check that Set Gravity Enabled works on an entity with gravity that starts as disabled -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14976307 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C14976308_ScriptCanvas_SetKinematicTargetTransform.py b/AutomatedTesting/Gem/PythonTests/physics/C14976308_ScriptCanvas_SetKinematicTargetTransform.py index 815e922a76..c76c5cab80 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C14976308_ScriptCanvas_SetKinematicTargetTransform.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C14976308_ScriptCanvas_SetKinematicTargetTransform.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test Case # ID : C14976308 # Title : Verify that SetKinematicTarget on PhysX rigid body updates transform for kinematic entities and vice versa -# URL : https://testrail.agscollab.com/index.php?/cases/view/14976308 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C15096732_Material_DefaultLibraryUpdatedAcrossLevels_after.py b/AutomatedTesting/Gem/PythonTests/physics/C15096732_Material_DefaultLibraryUpdatedAcrossLevels_after.py index 76ea9475ff..bdfc9d19be 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C15096732_Material_DefaultLibraryUpdatedAcrossLevels_after.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C15096732_Material_DefaultLibraryUpdatedAcrossLevels_after.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C15096732 # Test Case Title : Verify Default material library works across different levels -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15096732 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C15096732_Material_DefaultLibraryUpdatedAcrossLevels_before.py b/AutomatedTesting/Gem/PythonTests/physics/C15096732_Material_DefaultLibraryUpdatedAcrossLevels_before.py index 748052db56..d45423fe94 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C15096732_Material_DefaultLibraryUpdatedAcrossLevels_before.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C15096732_Material_DefaultLibraryUpdatedAcrossLevels_before.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C15096732 # Test Case Title : Verify Default material library works across different levels -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15096732 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C15096735_Materials_DefaultLibraryConsistency.py b/AutomatedTesting/Gem/PythonTests/physics/C15096735_Materials_DefaultLibraryConsistency.py index 4c7949cc91..3dd49d4fe4 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C15096735_Materials_DefaultLibraryConsistency.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C15096735_Materials_DefaultLibraryConsistency.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C15096735 # Test Case Title : Verify that default material library works consistently across all systems that use it -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15096735 + # fmt:off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C15096737_Materials_DefaultMaterialLibraryChanges.py b/AutomatedTesting/Gem/PythonTests/physics/C15096737_Materials_DefaultMaterialLibraryChanges.py index cc7dc3c5e2..c806cbb7d4 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C15096737_Materials_DefaultMaterialLibraryChanges.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C15096737_Materials_DefaultMaterialLibraryChanges.py @@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test Case Title : Verify that a change in the default material library material information # affects all the materials that reference it, even non-defaulted # exactly like if the library was selected -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15096737 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C15096740_Material_LibraryUpdatedCorrectly.py b/AutomatedTesting/Gem/PythonTests/physics/C15096740_Material_LibraryUpdatedCorrectly.py index 0f33f0858b..bef98f6830 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C15096740_Material_LibraryUpdatedCorrectly.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C15096740_Material_LibraryUpdatedCorrectly.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C15096740 Test Case Title : Verify that clearing a material library on all systems that use it, assigns the default material library -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15096740 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C15308217_NoCrash_LevelSwitch.py b/AutomatedTesting/Gem/PythonTests/physics/C15308217_NoCrash_LevelSwitch.py index 625a8bfb4a..dadfbd7a7e 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C15308217_NoCrash_LevelSwitch.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C15308217_NoCrash_LevelSwitch.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C15308217 # Test Case Title : Verify that the Terrain texture layer doesn't crash when changing # from on a level with a terrain component to another level without a terrain component -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15308217 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C15308221_Material_ComponentsInSyncWithLibrary.py b/AutomatedTesting/Gem/PythonTests/physics/C15308221_Material_ComponentsInSyncWithLibrary.py index 3f6457f15c..ebc7d3fadd 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C15308221_Material_ComponentsInSyncWithLibrary.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C15308221_Material_ComponentsInSyncWithLibrary.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test Case ID : C15308221 # Test Case Title : Verify that material library and slots are always in sync and work consistently through the different places of usage -# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/15308221 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C15425929_Undo_Redo.py b/AutomatedTesting/Gem/PythonTests/physics/C15425929_Undo_Redo.py index 57a0e6a75e..dba14552c8 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C15425929_Undo_Redo.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C15425929_Undo_Redo.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C15425929 # Test Case Title : Verify that undo - redo operations do not create any error -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15425929 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C15425935_Material_LibraryUpdatedAcrossLevels.py b/AutomatedTesting/Gem/PythonTests/physics/C15425935_Material_LibraryUpdatedAcrossLevels.py index f61886ccd0..8ff90fddaf 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C15425935_Material_LibraryUpdatedAcrossLevels.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C15425935_Material_LibraryUpdatedAcrossLevels.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C15425935 # Test Case Title : Verify that the change in Material Library gets updated across levels -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15425935 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C15556261_PhysXMaterials_CharacterControllerMaterialAssignment.py b/AutomatedTesting/Gem/PythonTests/physics/C15556261_PhysXMaterials_CharacterControllerMaterialAssignment.py index 99e0dcc669..21f5952678 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C15556261_PhysXMaterials_CharacterControllerMaterialAssignment.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C15556261_PhysXMaterials_CharacterControllerMaterialAssignment.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C15556261 # Test Case Title : Check that the material assignment works with Character Controller -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15556261 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C15563573_Material_AddModifyDeleteOnCharacterController.py b/AutomatedTesting/Gem/PythonTests/physics/C15563573_Material_AddModifyDeleteOnCharacterController.py index 4572dd322a..1e9b15fc90 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C15563573_Material_AddModifyDeleteOnCharacterController.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C15563573_Material_AddModifyDeleteOnCharacterController.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test Case ID : C15563573 # Test Case Title : Check that any change (Add/Delete/Modify) made to the material surface in the material library reflects immediately in the PhysX Character Controller -# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/15563573 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C15845879_ForceRegion_HighLinearDampingForce.py b/AutomatedTesting/Gem/PythonTests/physics/C15845879_ForceRegion_HighLinearDampingForce.py index 52af7d8be5..6b31ffb13b 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C15845879_ForceRegion_HighLinearDampingForce.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C15845879_ForceRegion_HighLinearDampingForce.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C15845879 # Test Case Title : Check that linear damping with high values do not make the object to quiver -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15845879 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C17411467_AddPhysxRagdollComponent.py b/AutomatedTesting/Gem/PythonTests/physics/C17411467_AddPhysxRagdollComponent.py index 68efd452b2..32bd02ae26 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C17411467_AddPhysxRagdollComponent.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C17411467_AddPhysxRagdollComponent.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C17411467 Test Case Title : Check that Physx Ragdoll component can be added without errors/warnings -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/17411467 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243580_Joints_Fixed2BodiesConstrained.py b/AutomatedTesting/Gem/PythonTests/physics/C18243580_Joints_Fixed2BodiesConstrained.py index b13869ce12..c3a351ebea 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243580_Joints_Fixed2BodiesConstrained.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243580_Joints_Fixed2BodiesConstrained.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18243580 # Test Case Title : Check that fixed joint constrains 2 bodies -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243580 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243581_Joints_FixedBreakable.py b/AutomatedTesting/Gem/PythonTests/physics/C18243581_Joints_FixedBreakable.py index 19feee575b..00e9a5625f 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243581_Joints_FixedBreakable.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243581_Joints_FixedBreakable.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18243581 # Test Case Title : Check that fixed joint is breakable -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243581 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243582_Joints_FixedLeadFollowerCollide.py b/AutomatedTesting/Gem/PythonTests/physics/C18243582_Joints_FixedLeadFollowerCollide.py index c9c90d35a7..d34e3f67d3 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243582_Joints_FixedLeadFollowerCollide.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243582_Joints_FixedLeadFollowerCollide.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18243582 # Test Case Title : Check that fixed joint allows lead-follower collision -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243582 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243583_Joints_Hinge2BodiesConstrained.py b/AutomatedTesting/Gem/PythonTests/physics/C18243583_Joints_Hinge2BodiesConstrained.py index 66343534a7..eafce6ceaf 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243583_Joints_Hinge2BodiesConstrained.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243583_Joints_Hinge2BodiesConstrained.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18243583 # Test Case Title : Check that hinge joint constrains 2 bodies about X-axis -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243583 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243584_Joints_HingeSoftLimitsConstrained.py b/AutomatedTesting/Gem/PythonTests/physics/C18243584_Joints_HingeSoftLimitsConstrained.py index 3b335cc5c6..90027be2d7 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243584_Joints_HingeSoftLimitsConstrained.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243584_Joints_HingeSoftLimitsConstrained.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18243584 # Test Case Title : Check that hinge joint allows soft limit constraints on 2 bodies -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243584 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243585_Joints_HingeNoLimitsConstrained.py b/AutomatedTesting/Gem/PythonTests/physics/C18243585_Joints_HingeNoLimitsConstrained.py index 289522fcac..0f677d94ae 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243585_Joints_HingeNoLimitsConstrained.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243585_Joints_HingeNoLimitsConstrained.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18243585 # Test Case Title : Check that hinge joint allows no limit constraints on 2 bodies -# URL of the test case :https://testrail.agscollab.com/index.php?/cases/view/18243585 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243586_Joints_HingeLeadFollowerCollide.py b/AutomatedTesting/Gem/PythonTests/physics/C18243586_Joints_HingeLeadFollowerCollide.py index 60b9189c83..7c973729d1 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243586_Joints_HingeLeadFollowerCollide.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243586_Joints_HingeLeadFollowerCollide.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18243586 # Test Case Title : Check that hinge joint allows lead-follower collision -# URL of the test case :https://testrail.agscollab.com/index.php?/cases/view/18243586 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243587_Joints_HingeBreakable.py b/AutomatedTesting/Gem/PythonTests/physics/C18243587_Joints_HingeBreakable.py index af6fa8cbac..4734328d70 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243587_Joints_HingeBreakable.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243587_Joints_HingeBreakable.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18243587 # Test Case Title : Check that hinge joint is breakable -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243587 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243588_Joints_Ball2BodiesConstrained.py b/AutomatedTesting/Gem/PythonTests/physics/C18243588_Joints_Ball2BodiesConstrained.py index 074ca03000..b6acfd6dd8 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243588_Joints_Ball2BodiesConstrained.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243588_Joints_Ball2BodiesConstrained.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18243588 # Test Case Title : Check that ball joint constrains 2 bodies within cone limits -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243588 + # fmt: off class Tests: enter_game_mode = ("Entered game mode", "Failed to enter game mode") diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243589_Joints_BallSoftLimitsConstrained.py b/AutomatedTesting/Gem/PythonTests/physics/C18243589_Joints_BallSoftLimitsConstrained.py index 16266b2da3..1ca49035b5 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243589_Joints_BallSoftLimitsConstrained.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243589_Joints_BallSoftLimitsConstrained.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : 18243589 # Test Case Title : Check that ball joint allows soft limit constraints -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243589 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243590_Joints_BallNoLimitsConstrained.py b/AutomatedTesting/Gem/PythonTests/physics/C18243590_Joints_BallNoLimitsConstrained.py index ede46fd5c5..1d5a213cd8 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243590_Joints_BallNoLimitsConstrained.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243590_Joints_BallNoLimitsConstrained.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18243590 # Test Case Title : Check that ball joint allows no limit constraints -# URL of the test case :https://testrail.agscollab.com/index.php?/cases/view/18243590 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243591_Joints_BallLeadFollowerCollide.py b/AutomatedTesting/Gem/PythonTests/physics/C18243591_Joints_BallLeadFollowerCollide.py index c3e527956f..8f7b2d7823 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243591_Joints_BallLeadFollowerCollide.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243591_Joints_BallLeadFollowerCollide.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18243591 # Test Case Title : Check that ball joint allows lead-follower collision -# URL of the test case :https://testrail.agscollab.com/index.php?/cases/view/18243591 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243592_Joints_BallBreakable.py b/AutomatedTesting/Gem/PythonTests/physics/C18243592_Joints_BallBreakable.py index 350fccd125..0fa43cf9c8 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243592_Joints_BallBreakable.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243592_Joints_BallBreakable.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18243592 # Test Case Title : Check that ball joint is breakable -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243592 + # fmt: off class Tests: enter_game_mode = ("Entered game mode", "Failed to enter game mode") diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18243593_Joints_GlobalFrameConstrained.py b/AutomatedTesting/Gem/PythonTests/physics/C18243593_Joints_GlobalFrameConstrained.py index 4580a229ea..37d659f181 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18243593_Joints_GlobalFrameConstrained.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18243593_Joints_GlobalFrameConstrained.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18243593 # Test Case Title : Check that fixed/hinge/ball joints allow constraints to global frame -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243593 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18977601_Material_FrictionCombinePriority.py b/AutomatedTesting/Gem/PythonTests/physics/C18977601_Material_FrictionCombinePriority.py index 57dfc0f349..b17b97a816 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18977601_Material_FrictionCombinePriority.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18977601_Material_FrictionCombinePriority.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18977601 # Test Case Title : Verify that when two objects with different materials collide, the friction combine priority works -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18977601 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C18981526_Material_RestitutionCombinePriority.py b/AutomatedTesting/Gem/PythonTests/physics/C18981526_Material_RestitutionCombinePriority.py index 9b7fea1ab9..690d3d4d3f 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C18981526_Material_RestitutionCombinePriority.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C18981526_Material_RestitutionCombinePriority.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C18981526 # Test Case Title : Verify when two objects with different materials collide, the restitution combine priority works -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18981526 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C19536274_GetCollisionName_PrintsName.py b/AutomatedTesting/Gem/PythonTests/physics/C19536274_GetCollisionName_PrintsName.py index 48c947aa1f..611b502019 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C19536274_GetCollisionName_PrintsName.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C19536274_GetCollisionName_PrintsName.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C19536274 Test Case Title : Verify that the Get Collision Layer Name node prints the name of the collision layer -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/19536274 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C19536277_GetCollisionName_PrintsNothing.py b/AutomatedTesting/Gem/PythonTests/physics/C19536277_GetCollisionName_PrintsNothing.py index 66e1302b5b..04d973cbb2 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C19536277_GetCollisionName_PrintsNothing.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C19536277_GetCollisionName_PrintsNothing.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C19536277 Test Case Title : Verify that when a group is modified using ToggleCollisionLayer node such that the new group is not in the pre-existing groups, GetCollisionGroupName node prints no value -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/19536277 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C19578018_ShapeColliderWithNoShapeComponent.py b/AutomatedTesting/Gem/PythonTests/physics/C19578018_ShapeColliderWithNoShapeComponent.py index 32ece8541c..23996934bc 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C19578018_ShapeColliderWithNoShapeComponent.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C19578018_ShapeColliderWithNoShapeComponent.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C19578018 Test Case Title : Verify that a shape collider component with no shape component indicates a missing service -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/19578018 + """ # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C19578021_ShapeCollider_CanBeAdded.py b/AutomatedTesting/Gem/PythonTests/physics/C19578021_ShapeCollider_CanBeAdded.py index 0c42f5457d..1eb36e16f4 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C19578021_ShapeCollider_CanBeAdded.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C19578021_ShapeCollider_CanBeAdded.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C19578021 Test Case Title : Verify that a shape collider component may be added to an entity along with one or more PhysX collider components -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/19578021 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C19723164_ShapeColliders_WontCrashEditor.py b/AutomatedTesting/Gem/PythonTests/physics/C19723164_ShapeColliders_WontCrashEditor.py index 769f08e3c4..f6584624c3 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C19723164_ShapeColliders_WontCrashEditor.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C19723164_ShapeColliders_WontCrashEditor.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C19723164 Test Case Title : Verify that if we had 512 shape colliders in the level, the level does not crash -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/19723164 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C24308873_CylinderShapeCollider_CollidesWithPhysXTerrain.py b/AutomatedTesting/Gem/PythonTests/physics/C24308873_CylinderShapeCollider_CollidesWithPhysXTerrain.py index 60fa9dc44d..101b96b99b 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C24308873_CylinderShapeCollider_CollidesWithPhysXTerrain.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C24308873_CylinderShapeCollider_CollidesWithPhysXTerrain.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C24308873 # Test Case Title : Check that cylinder shape collider collides with terrain -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/24308873 + # A cylinder is suspended slightly over PhysX Terrain to check that it collides when dropped diff --git a/AutomatedTesting/Gem/PythonTests/physics/C28978033_Ragdoll_WorldBodyBusTests.py b/AutomatedTesting/Gem/PythonTests/physics/C28978033_Ragdoll_WorldBodyBusTests.py index 8980e68250..052795318a 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C28978033_Ragdoll_WorldBodyBusTests.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C28978033_Ragdoll_WorldBodyBusTests.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C28978033 # Test Case Title : Check that WorldRequestBus works with PhysX ragdoll -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/28978033 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C29032500_EditorComponents_WorldBodyBusWorks.py b/AutomatedTesting/Gem/PythonTests/physics/C29032500_EditorComponents_WorldBodyBusWorks.py index be541e4bf9..4915d362f7 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C29032500_EditorComponents_WorldBodyBusWorks.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C29032500_EditorComponents_WorldBodyBusWorks.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C29032500 # Test Case Title : Check that WorldRequestBus works with editor components -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/29032500 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C3510642_Terrain_NotCollideWithTerrain.py b/AutomatedTesting/Gem/PythonTests/physics/C3510642_Terrain_NotCollideWithTerrain.py index 3e3770adb9..b671cd3754 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C3510642_Terrain_NotCollideWithTerrain.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C3510642_Terrain_NotCollideWithTerrain.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C3510642 # Test Case Title : Check that when no physX terrain component is added, collision of a PhysX object # with terrain does not work. Consequently, PhysX material assignment to terrain cannot be tested. -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/3510642 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4044455_Material_libraryChangesInstantly.py b/AutomatedTesting/Gem/PythonTests/physics/C4044455_Material_libraryChangesInstantly.py index 5f2c40f11e..a7916129e8 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4044455_Material_libraryChangesInstantly.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4044455_Material_libraryChangesInstantly.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : 4044455 # Test Case Title : Verify that any change in any of the values including the name of the material, # once saved, is immediately reflected in the component and functionality -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044455 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4044456_Material_FrictionCombine.py b/AutomatedTesting/Gem/PythonTests/physics/C4044456_Material_FrictionCombine.py index b71c1e8ede..eced55fc76 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4044456_Material_FrictionCombine.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4044456_Material_FrictionCombine.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4044456 # Test Case Title : Verify that when two objects with different materials collide, the friction combine works -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044456 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4044457_Material_RestitutionCombine.py b/AutomatedTesting/Gem/PythonTests/physics/C4044457_Material_RestitutionCombine.py index 1ed2e8086c..5969541a86 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4044457_Material_RestitutionCombine.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4044457_Material_RestitutionCombine.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4044457 # Test Case Title : Verify that when two objects with different materials collide, the restitution combine works -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044457 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4044459_Material_DynamicFriction.py b/AutomatedTesting/Gem/PythonTests/physics/C4044459_Material_DynamicFriction.py index c420250dfa..d414bc4813 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4044459_Material_DynamicFriction.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4044459_Material_DynamicFriction.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4044459 # Test Case Title : Verify the functionality of dynamic friction -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044459 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4044460_Material_StaticFriction.py b/AutomatedTesting/Gem/PythonTests/physics/C4044460_Material_StaticFriction.py index 4e330002ff..6e864c554b 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4044460_Material_StaticFriction.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4044460_Material_StaticFriction.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4044460 # Test Case Title : Verify the functionality of static friction -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044460 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4044461_Material_Restitution.py b/AutomatedTesting/Gem/PythonTests/physics/C4044461_Material_Restitution.py index af8990d87f..a5219ed2c7 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4044461_Material_Restitution.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4044461_Material_Restitution.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4044461 # Test Case Title : Verify the functionality of restitution -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044461 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4044694_Material_EmptyLibraryUsesDefault.py b/AutomatedTesting/Gem/PythonTests/physics/C4044694_Material_EmptyLibraryUsesDefault.py index a8375114d5..e9106d89eb 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4044694_Material_EmptyLibraryUsesDefault.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4044694_Material_EmptyLibraryUsesDefault.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test Case ID : C4044694 # Test Case Title : Verify that if we add an empty Material library in Collider Component, the object continues to use Default material values -# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/4044694 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4044695_PhysXCollider_AddMultipleSurfaceFbx.py b/AutomatedTesting/Gem/PythonTests/physics/C4044695_PhysXCollider_AddMultipleSurfaceFbx.py index 858e778f07..57cdc8f9c5 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4044695_PhysXCollider_AddMultipleSurfaceFbx.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4044695_PhysXCollider_AddMultipleSurfaceFbx.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C4044695 Test Case Title : Verify that when you add a multiple surface fbx in PxMesh in PhysxCollider, multiple number of Material Slots populate in the Materials Section -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044695 + """ @@ -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/C4044697_Material_PerfaceMaterialValidation.py b/AutomatedTesting/Gem/PythonTests/physics/C4044697_Material_PerfaceMaterialValidation.py index dba759015f..435e8242d5 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4044697_Material_PerfaceMaterialValidation.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4044697_Material_PerfaceMaterialValidation.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4044697 # Test Case Title : Verify that each surface picks up the material assigned to it and behaves accordingly. -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044697 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4888315_Material_AddModifyDeleteOnCollider.py b/AutomatedTesting/Gem/PythonTests/physics/C4888315_Material_AddModifyDeleteOnCollider.py index 145a5e1b60..b7b3ed855f 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4888315_Material_AddModifyDeleteOnCollider.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4888315_Material_AddModifyDeleteOnCollider.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test Case ID : C4888315 # Test Case Title : Check that any change (Add/Delete/Modify) made to the material surface in the material library reflects immediately in the PhysX Collider component -# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/4888315 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4925577_Materials_MaterialAssignedToTerrain.py b/AutomatedTesting/Gem/PythonTests/physics/C4925577_Materials_MaterialAssignedToTerrain.py index 59b6a3fdfc..7ebb62eaf3 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4925577_Materials_MaterialAssignedToTerrain.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4925577_Materials_MaterialAssignedToTerrain.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4925577 # Test Case Title : Verify that material can be assigned to PhysX terrain in Terrain Texture Layers -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4925577 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4925579_Material_AddModifyDeleteOnTerrain.py b/AutomatedTesting/Gem/PythonTests/physics/C4925579_Material_AddModifyDeleteOnTerrain.py index e0425c35be..315cf9c3bd 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4925579_Material_AddModifyDeleteOnTerrain.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4925579_Material_AddModifyDeleteOnTerrain.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test Case ID : C4925579 # Test Case Title : Check that any change (Add/Delete/Modify) made to the material surface in the material library reflects immediately in the PhysX Terrain layers -# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/4925579 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4925580_Material_RagdollBonesMaterial.py b/AutomatedTesting/Gem/PythonTests/physics/C4925580_Material_RagdollBonesMaterial.py index 9944a033e3..5fae373f47 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4925580_Material_RagdollBonesMaterial.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4925580_Material_RagdollBonesMaterial.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test Case ID : C4925580 # Test Case Title : Verify that Material can be assigned to Ragdoll Bones and they behave as per their material -# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/4925580 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4925582_Material_AddModifyDeleteOnRagdollBones.py b/AutomatedTesting/Gem/PythonTests/physics/C4925582_Material_AddModifyDeleteOnRagdollBones.py index ed872ae99e..ca37be6ff0 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4925582_Material_AddModifyDeleteOnRagdollBones.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4925582_Material_AddModifyDeleteOnRagdollBones.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test Case ID : C4925582 # Test Case Title : Check that any change (Add/Delete/Modify) made to the material surface in the material library reflects immediately in the ragdoll bones -# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/4925582 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976194_RigidBody_PhysXComponentIsValid.py b/AutomatedTesting/Gem/PythonTests/physics/C4976194_RigidBody_PhysXComponentIsValid.py index aa059eb224..8bb005711e 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976194_RigidBody_PhysXComponentIsValid.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976194_RigidBody_PhysXComponentIsValid.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976194 # Test Case Title : Verify that you can add PhysX Rigid Bodies Physics component to an Entity without any warning or Error. -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976194 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976195_RigidBodies_InitialLinearVelocity.py b/AutomatedTesting/Gem/PythonTests/physics/C4976195_RigidBodies_InitialLinearVelocity.py index c5740b2798..3dbf26a0ac 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976195_RigidBodies_InitialLinearVelocity.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976195_RigidBodies_InitialLinearVelocity.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976195 # Test Case Title : Verify that when you assign an Initial Linear Velocity to an object, # ... it moves with that linear velocity when we switch to game mode. -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976195 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976197_RigidBodies_InitialAngularVelocity.py b/AutomatedTesting/Gem/PythonTests/physics/C4976197_RigidBodies_InitialAngularVelocity.py index 08e171269e..7a17eb5941 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976197_RigidBodies_InitialAngularVelocity.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976197_RigidBodies_InitialAngularVelocity.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976197 # Test Case Title : Verify that when you assign an Initial Angular Velocity to an object, # it moves with that Angular velocity when we switch to game mode -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976197 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976199_RigidBodies_LinearDampingObjectMotion.py b/AutomatedTesting/Gem/PythonTests/physics/C4976199_RigidBodies_LinearDampingObjectMotion.py index 2e19b633c4..457cb18640 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976199_RigidBodies_LinearDampingObjectMotion.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976199_RigidBodies_LinearDampingObjectMotion.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976199 # Test Case Title : Verify that with higher linear damping, the object in motion comes to rest faster -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976199 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976200_RigidBody_AngularDampingObjectRotation.py b/AutomatedTesting/Gem/PythonTests/physics/C4976200_RigidBody_AngularDampingObjectRotation.py index b90b34be53..f423821757 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976200_RigidBody_AngularDampingObjectRotation.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976200_RigidBody_AngularDampingObjectRotation.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976200 # Test Case Title : Verify that with higher angular damping, the object in rotation comes to rest faster -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976200 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976201_RigidBody_MassIsAssigned.py b/AutomatedTesting/Gem/PythonTests/physics/C4976201_RigidBody_MassIsAssigned.py index 7fb0ff84ce..fd15cbbb0f 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976201_RigidBody_MassIsAssigned.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976201_RigidBody_MassIsAssigned.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976201 # Test Case Title : Verify that the value assigned to the Mass of the object, gets actually assigned to the object -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976201 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976202_RigidBody_StopsWhenBelowKineticThreshold.py b/AutomatedTesting/Gem/PythonTests/physics/C4976202_RigidBody_StopsWhenBelowKineticThreshold.py index 7d4cf1066e..c66f5cf7b6 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976202_RigidBody_StopsWhenBelowKineticThreshold.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976202_RigidBody_StopsWhenBelowKineticThreshold.py @@ -14,7 +14,7 @@ Test case ID : C4976202 Test Case Title : Verify that if the object is moving with Kinetic energy less than the sleep threshold value, then physX will put it to stop after 0.4 secs (once the wake counter goes to zero) if the KE is still below the threshold -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976202 + """ # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976204_Verify_Start_Asleep_Condition.py b/AutomatedTesting/Gem/PythonTests/physics/C4976204_Verify_Start_Asleep_Condition.py index 29fc2ec2a3..6b29062adf 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976204_Verify_Start_Asleep_Condition.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976204_Verify_Start_Asleep_Condition.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976204 # Test Case Title : Verify that when Start Asleep is checked, the object in air does not fall down due to # gravity or does not start moving with initial linear velocity assigned to it when switched to game mode -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976204 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976206_RigidBodies_GravityEnabledActive.py b/AutomatedTesting/Gem/PythonTests/physics/C4976206_RigidBodies_GravityEnabledActive.py index b97c590a93..934e546d65 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976206_RigidBodies_GravityEnabledActive.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976206_RigidBodies_GravityEnabledActive.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976206 # Test Case Title : VErify that when Gravity enables is checked, the object falls down due to gravity [sic] -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976206 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976207_PhysXRigidBodies_KinematicBehavior.py b/AutomatedTesting/Gem/PythonTests/physics/C4976207_PhysXRigidBodies_KinematicBehavior.py index 4481c00fc5..58193a1e9b 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976207_PhysXRigidBodies_KinematicBehavior.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976207_PhysXRigidBodies_KinematicBehavior.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976207 # Test Case Title : Verify that when Kinematic is checked, the object behaves as a Kinematic object -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976207 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976209_RigidBody_ComputesCOM.py b/AutomatedTesting/Gem/PythonTests/physics/C4976209_RigidBody_ComputesCOM.py index 79ea6e5dc7..600b20d98d 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976209_RigidBody_ComputesCOM.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976209_RigidBody_ComputesCOM.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976209 # Test Case Title : Verify that when Compute COM is enabled, the PhysX system computes the COM of the object on its own -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976209 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976210_COM_ManualSetting.py b/AutomatedTesting/Gem/PythonTests/physics/C4976210_COM_ManualSetting.py index 451081ba23..c53fb3508d 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976210_COM_ManualSetting.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976210_COM_ManualSetting.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976210 # Test Case Title : Verify that when Compute COM is disabled, the user gets an option to add the co-ordinates of # the COM and the COM gets implemented at those co-ordinates. -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976210 + import os diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976218_RigidBodies_InertiaObjectsNotComputed.py b/AutomatedTesting/Gem/PythonTests/physics/C4976218_RigidBodies_InertiaObjectsNotComputed.py index 63f0d382c3..dada67548b 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976218_RigidBodies_InertiaObjectsNotComputed.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976218_RigidBodies_InertiaObjectsNotComputed.py @@ -9,7 +9,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ # Test case ID : C4976218 # Test Case Title: Verify that when compute inertia is checked, the physX engine does compute the inertia of the objects -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976218 + # fmt: off class Tests(): diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976227_Collider_NewGroup.py b/AutomatedTesting/Gem/PythonTests/physics/C4976227_Collider_NewGroup.py index 8d9dc01396..78c031870d 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976227_Collider_NewGroup.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976227_Collider_NewGroup.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976227 # Test Case Title : Validate that a Collision Group can be added -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976227 + # Level has entity with custom collision group added. # If level enters game mode, collision group addition is validated. diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976236_AddPhysxColliderComponent.py b/AutomatedTesting/Gem/PythonTests/physics/C4976236_AddPhysxColliderComponent.py index 72442601e4..fdb862005f 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976236_AddPhysxColliderComponent.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976236_AddPhysxColliderComponent.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C4976236 Test Case Title : Verify that you can add the physX collider component to an entity without it throwing an error or warning -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976236 + """ # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976242_Collision_SameCollisionlayerSameCollisiongroup.py b/AutomatedTesting/Gem/PythonTests/physics/C4976242_Collision_SameCollisionlayerSameCollisiongroup.py index bb6234b69e..ac14164f45 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976242_Collision_SameCollisionlayerSameCollisiongroup.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976242_Collision_SameCollisionlayerSameCollisiongroup.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976242 # Test Case Title : Assign same collision layer and same collision group to two entities and # verify that they collide or not -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976242 + # fmt: off class Tests(): diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976243_Collision_SameCollisionGroupDiffCollisionLayers.py b/AutomatedTesting/Gem/PythonTests/physics/C4976243_Collision_SameCollisionGroupDiffCollisionLayers.py index 949ebb2b2a..44a8b15169 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976243_Collision_SameCollisionGroupDiffCollisionLayers.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976243_Collision_SameCollisionGroupDiffCollisionLayers.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976243 # Test Case Title : Assign different collision layers and same collision group # (such that this group has both these collision layers enabled) to two entities and verify that they collide -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976243 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976244_Collider_SameGroupSameLayerCollision.py b/AutomatedTesting/Gem/PythonTests/physics/C4976244_Collider_SameGroupSameLayerCollision.py index c35b9fab81..ca1e7f57f5 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976244_Collider_SameGroupSameLayerCollision.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976244_Collider_SameGroupSameLayerCollision.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976244 # Test Case Title : Checks that two entities of similar custom layer collide -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976244 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4976245_PhysXCollider_CollisionLayerTest.py b/AutomatedTesting/Gem/PythonTests/physics/C4976245_PhysXCollider_CollisionLayerTest.py index dafb8d9014..5f8c0a8d67 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4976245_PhysXCollider_CollisionLayerTest.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4976245_PhysXCollider_CollisionLayerTest.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4976245 # Test Case Title : Check that two entities of collision group "None" do not collide, # even though they have the same collision layer -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976245 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4982593_PhysXCollider_CollisionLayerTest.py b/AutomatedTesting/Gem/PythonTests/physics/C4982593_PhysXCollider_CollisionLayerTest.py index 8d985de402..126ebf48dc 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4982593_PhysXCollider_CollisionLayerTest.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4982593_PhysXCollider_CollisionLayerTest.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4982593 # Test Case Title : Check that two entities with different collision groups and layers do not collide. -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4982593 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4982595_Collider_TriggerDisablesCollision.py b/AutomatedTesting/Gem/PythonTests/physics/C4982595_Collider_TriggerDisablesCollision.py index 8746996ca8..c50c517086 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4982595_Collider_TriggerDisablesCollision.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4982595_Collider_TriggerDisablesCollision.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test Case ID : C4982595 # Test Case Title : Verify that when the Trigger Checkbox is ticked, the object no longer collides with another object # but simply passes through it -# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/4982595 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4982797_Collider_ColliderOffset.py b/AutomatedTesting/Gem/PythonTests/physics/C4982797_Collider_ColliderOffset.py index 2bd4fc5adc..29a3b7f89d 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4982797_Collider_ColliderOffset.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4982797_Collider_ColliderOffset.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4982797 # Test Case Title : Check that collision offsets trigger collision events, # not entity transform locations -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4982797 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4982798_Collider_ColliderRotationOffset.py b/AutomatedTesting/Gem/PythonTests/physics/C4982798_Collider_ColliderRotationOffset.py index f86890d8bf..6276c81871 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4982798_Collider_ColliderRotationOffset.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4982798_Collider_ColliderRotationOffset.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C4982798 # Test Case Title : Verify that when the x,y,z values are defined in the offset, the collider frame # rotates from its original orientation in the direction defined by the x,y,z units -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4982798 + import os diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4982800_PhysXColliderShape_CanBeSelected.py b/AutomatedTesting/Gem/PythonTests/physics/C4982800_PhysXColliderShape_CanBeSelected.py index c37ad2b2d2..1822a1efa7 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4982800_PhysXColliderShape_CanBeSelected.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4982800_PhysXColliderShape_CanBeSelected.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C4982800 Test Case Title : Verify that the shape Sphere can be selected from the drop downlist and the value for its radius can be set -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4982800 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4982801_PhysXColliderShape_CanBeSelected.py b/AutomatedTesting/Gem/PythonTests/physics/C4982801_PhysXColliderShape_CanBeSelected.py index 19a835da18..ef9b8171c4 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4982801_PhysXColliderShape_CanBeSelected.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4982801_PhysXColliderShape_CanBeSelected.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C4982801 Test Case Title : Verify that the shape Box can be selected from drop downlist and the value for its dimensions in x,y,z can be set after that -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4982801 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4982802_PhysXColliderShape_CanBeSelected.py b/AutomatedTesting/Gem/PythonTests/physics/C4982802_PhysXColliderShape_CanBeSelected.py index e1365d0887..642dea48de 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4982802_PhysXColliderShape_CanBeSelected.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4982802_PhysXColliderShape_CanBeSelected.py @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C4982802 Test Case Title : Verify that the shape capsule can be selected from drop downlist and the value for its height and radius can be set after that -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4982802 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C4982803_Enable_PxMesh_Option.py b/AutomatedTesting/Gem/PythonTests/physics/C4982803_Enable_PxMesh_Option.py index 4ae177934a..99e93c26c9 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C4982803_Enable_PxMesh_Option.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C4982803_Enable_PxMesh_Option.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Test case ID : C4982803 Test Case Title : Verify that when the shape Physics Asset is selected, PxMesh option gets enabled and a Px Mesh can be selected and assigned to the object -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4982803 + """ # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5296614_PhysXMaterial_ColliderShape.py b/AutomatedTesting/Gem/PythonTests/physics/C5296614_PhysXMaterial_ColliderShape.py index 9d2a49ac70..b4af6ab506 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5296614_PhysXMaterial_ColliderShape.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5296614_PhysXMaterial_ColliderShape.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5296614 # Test Case Title : Check that unless you assign a shape to a physX collider component, # the material assigned to it does not take affect -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5296614 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5340400_RigidBody_ManualMomentOfInertia.py b/AutomatedTesting/Gem/PythonTests/physics/C5340400_RigidBody_ManualMomentOfInertia.py index 7617e41cad..838007436c 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5340400_RigidBody_ManualMomentOfInertia.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5340400_RigidBody_ManualMomentOfInertia.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5340400 # Test Case Title : Verify that when Compute inertia is disabled, the user gets to set the moment of inertia # and physX engine work accordingly -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5340400 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5689518_PhysXTerrain_CollidesWithPhysXTerrain.py b/AutomatedTesting/Gem/PythonTests/physics/C5689518_PhysXTerrain_CollidesWithPhysXTerrain.py index 0686adf15d..f17bf2492f 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5689518_PhysXTerrain_CollidesWithPhysXTerrain.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5689518_PhysXTerrain_CollidesWithPhysXTerrain.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5689518 # Test Case Title : PhysX entities collide with PhysX Terrain -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5689518 + # A ball is suspended slightly over PhysX Terrain to check that it collides when dropped diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5689522_Physxterrain_AddPhysxterrainNoEditorCrash.py b/AutomatedTesting/Gem/PythonTests/physics/C5689522_Physxterrain_AddPhysxterrainNoEditorCrash.py index ffafeaab34..61ef63c4ae 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5689522_Physxterrain_AddPhysxterrainNoEditorCrash.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5689522_Physxterrain_AddPhysxterrainNoEditorCrash.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5689522 # Test Case Title : Create an entity with PhysX terrain. Add another PhysX terrain and verify that # you are able to add it without any crash or error. -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5689522 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5689524_MultipleTerrains_CheckWarningInConsole.py b/AutomatedTesting/Gem/PythonTests/physics/C5689524_MultipleTerrains_CheckWarningInConsole.py index 2b1a73f1f3..c395507ea0 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5689524_MultipleTerrains_CheckWarningInConsole.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5689524_MultipleTerrains_CheckWarningInConsole.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5689524 # Test Case Title : Create multiple entities each with one or more terrain components and verify that you are # able to successfully add the PhysX Terrain components to them. -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5689524 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5689528_Terrain_MultipleTerrainComponents.py b/AutomatedTesting/Gem/PythonTests/physics/C5689528_Terrain_MultipleTerrainComponents.py index 265836489d..12f3955bf6 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5689528_Terrain_MultipleTerrainComponents.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5689528_Terrain_MultipleTerrainComponents.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5689528 # Test Case Title : Create multiple entities each with one PhysX terrain component and verify that a warning # is thrown to the user -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5689528 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5689529_Verify_Terrain_RigidBody_Collider_Mesh.py b/AutomatedTesting/Gem/PythonTests/physics/C5689529_Verify_Terrain_RigidBody_Collider_Mesh.py index 138ae68408..c5342d7421 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5689529_Verify_Terrain_RigidBody_Collider_Mesh.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5689529_Verify_Terrain_RigidBody_Collider_Mesh.py @@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test Case Title : Create an entity with PhysX Terrain component and add # PhysX Rigid Body PhysX, PhysX Collider and Rendering Mesh to it and # verify that it works in game mode -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5689529 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5689531_Warning_TerrainSliceTerrainComponent.py b/AutomatedTesting/Gem/PythonTests/physics/C5689531_Warning_TerrainSliceTerrainComponent.py index 7d60a61ec1..85a9037a36 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5689531_Warning_TerrainSliceTerrainComponent.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5689531_Warning_TerrainSliceTerrainComponent.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5689531 # Test Case Title : Check that when you add a spawner component to a level to spawn a # terrain and also add a terrain component explicitly, no crash happens -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5689531 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5932040_ForceRegion_CubeExertsWorldForce.py b/AutomatedTesting/Gem/PythonTests/physics/C5932040_ForceRegion_CubeExertsWorldForce.py index f01dd4582f..b1f9f8f934 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5932040_ForceRegion_CubeExertsWorldForce.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5932040_ForceRegion_CubeExertsWorldForce.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5932040 # Test Case Title : Check that force region exerts world space force on rigid bodies -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5932040 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5932041_PhysXForceRegion_LocalSpaceForceOnRigidBodies.py b/AutomatedTesting/Gem/PythonTests/physics/C5932041_PhysXForceRegion_LocalSpaceForceOnRigidBodies.py index 095a669541..5fc07fdc67 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5932041_PhysXForceRegion_LocalSpaceForceOnRigidBodies.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5932041_PhysXForceRegion_LocalSpaceForceOnRigidBodies.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5932041 # Test Case Title : Check that force region exerts local space force on rigid bodies -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5932041 + # Sphere drops and is acted upon in an upward and positive x-ward direction by a force # with a magnitude close to the assigned force region magnitude when it reaches the force region. diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5932042_PhysXForceRegion_LinearDamping.py b/AutomatedTesting/Gem/PythonTests/physics/C5932042_PhysXForceRegion_LinearDamping.py index 21d386954d..b55ce59d7a 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5932042_PhysXForceRegion_LinearDamping.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5932042_PhysXForceRegion_LinearDamping.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5932042 # Test Case Title : Check that force region exerts linear damping force on rigid bodies -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5932042 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5932043_ForceRegion_SimpleDragOnRigidBodies.py b/AutomatedTesting/Gem/PythonTests/physics/C5932043_ForceRegion_SimpleDragOnRigidBodies.py index 512698ce4b..b4d29f8065 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5932043_ForceRegion_SimpleDragOnRigidBodies.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5932043_ForceRegion_SimpleDragOnRigidBodies.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5932043 # Test Case Title : Check that force region exerts simple drag force on rigid bodies -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5932043 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5932044_ForceRegion_PointForceOnRigidBody.py b/AutomatedTesting/Gem/PythonTests/physics/C5932044_ForceRegion_PointForceOnRigidBody.py index b7257b488b..6cfaf857a4 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5932044_ForceRegion_PointForceOnRigidBody.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5932044_ForceRegion_PointForceOnRigidBody.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5932044 # Test Case Title : Check that force region exerts point force on rigid bodies -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5932044 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5932045_ForceRegion_Spline.py b/AutomatedTesting/Gem/PythonTests/physics/C5932045_ForceRegion_Spline.py index eca0679921..298265966c 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5932045_ForceRegion_Spline.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5932045_ForceRegion_Spline.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5932045 # Test Case Title : Check that force region exerts spline follow force on rigid bodies -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5932045 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5959759_RigidBody_ForceRegionSpherePointForce.py b/AutomatedTesting/Gem/PythonTests/physics/C5959759_RigidBody_ForceRegionSpherePointForce.py index 6aff0a7145..a8ef614f75 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5959759_RigidBody_ForceRegionSpherePointForce.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5959759_RigidBody_ForceRegionSpherePointForce.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5959759 # Test Case Title : Check that force region (sphere) exerts point force -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5959759 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5959760_PhysXForceRegion_PointForceExertion.py b/AutomatedTesting/Gem/PythonTests/physics/C5959760_PhysXForceRegion_PointForceExertion.py index 89eb1842be..4cb1cffd16 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5959760_PhysXForceRegion_PointForceExertion.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5959760_PhysXForceRegion_PointForceExertion.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5959760 # Test Case Title : Check that force region (capsule) exerts point force -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5959760 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5959761_ForceRegion_PhysAssetExertsPointForce.py b/AutomatedTesting/Gem/PythonTests/physics/C5959761_ForceRegion_PhysAssetExertsPointForce.py index ba0475266b..27f9e41a7d 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5959761_ForceRegion_PhysAssetExertsPointForce.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5959761_ForceRegion_PhysAssetExertsPointForce.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5959761 # Test Case Title : Check that force region (physics asset) exerts point force -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5959761 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5959763_ForceRegion_ForceRegionImpulsesCube.py b/AutomatedTesting/Gem/PythonTests/physics/C5959763_ForceRegion_ForceRegionImpulsesCube.py index 76130d25eb..4b62f9a029 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5959763_ForceRegion_ForceRegionImpulsesCube.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5959763_ForceRegion_ForceRegionImpulsesCube.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5959764 # Test Case Title : Check that rigid body (Cube) gets impulse from force region -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5959764 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5959764_ForceRegion_ForceRegionImpulsesCapsule.py b/AutomatedTesting/Gem/PythonTests/physics/C5959764_ForceRegion_ForceRegionImpulsesCapsule.py index f314dae5b7..aadc07b09b 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5959764_ForceRegion_ForceRegionImpulsesCapsule.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5959764_ForceRegion_ForceRegionImpulsesCapsule.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5959764 # Test Case Title : Check that rigid body (Capsule) gets impulse from force region -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5959764 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5959765_ForceRegion_AssetGetsImpulsed.py b/AutomatedTesting/Gem/PythonTests/physics/C5959765_ForceRegion_AssetGetsImpulsed.py index ddcba5996b..ed007594c2 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5959765_ForceRegion_AssetGetsImpulsed.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5959765_ForceRegion_AssetGetsImpulsed.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5959765 # Test Case Title : Check that rigid body (asset) gets impulse from force region -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5959765 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5959808_ForceRegion_PositionOffset.py b/AutomatedTesting/Gem/PythonTests/physics/C5959808_ForceRegion_PositionOffset.py index 175f73a420..7c3f8a43aa 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5959808_ForceRegion_PositionOffset.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5959808_ForceRegion_PositionOffset.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5959808 # Test Case Title : Verify Force Region Position Offset -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5959808 + # fmt:off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5959809_ForceRegion_RotationalOffset.py b/AutomatedTesting/Gem/PythonTests/physics/C5959809_ForceRegion_RotationalOffset.py index 1ad53194d3..dc84be8798 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5959809_ForceRegion_RotationalOffset.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5959809_ForceRegion_RotationalOffset.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5959809 # Test Case Title : Verify Force Region Rotational Offset -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5959809 + # fmt:off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5959810_ForceRegion_ForceRegionCombinesForces.py b/AutomatedTesting/Gem/PythonTests/physics/C5959810_ForceRegion_ForceRegionCombinesForces.py index 212f7cc86b..8efc3dc635 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5959810_ForceRegion_ForceRegionCombinesForces.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5959810_ForceRegion_ForceRegionCombinesForces.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5959810 # Test Case Title : Check that multiple forces in single force region create correct net force -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5959810 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5968759_ForceRegion_ExertsSeveralForcesOnRigidBody.py b/AutomatedTesting/Gem/PythonTests/physics/C5968759_ForceRegion_ExertsSeveralForcesOnRigidBody.py index 4aa0fef642..948e172962 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5968759_ForceRegion_ExertsSeveralForcesOnRigidBody.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5968759_ForceRegion_ExertsSeveralForcesOnRigidBody.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5968759 # Test Case Title : Check nested force regions exert forces simultaneously on rigid body -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5968759 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C5968760_ForceRegion_CheckNetForceChange.py b/AutomatedTesting/Gem/PythonTests/physics/C5968760_ForceRegion_CheckNetForceChange.py index ba491c96a7..0897fcb0d7 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C5968760_ForceRegion_CheckNetForceChange.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C5968760_ForceRegion_CheckNetForceChange.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C5968760 # Test Case Title : Check moving force region changes net force -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5968760 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C6032082_Terrain_MultipleResolutionsValid.py b/AutomatedTesting/Gem/PythonTests/physics/C6032082_Terrain_MultipleResolutionsValid.py index 4543b0b8f6..00d53a2d6c 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C6032082_Terrain_MultipleResolutionsValid.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C6032082_Terrain_MultipleResolutionsValid.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C6032082 # Test Case Title : Verify multiple terrain resolutions are supported -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/6032082 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C6090546_ForceRegion_SliceFileInstantiates.py b/AutomatedTesting/Gem/PythonTests/physics/C6090546_ForceRegion_SliceFileInstantiates.py index 557b7e7ec5..2b427468b7 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C6090546_ForceRegion_SliceFileInstantiates.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C6090546_ForceRegion_SliceFileInstantiates.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C6090546 # Test Case Title : Check that a force region slice can be saved and instantiated -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/6090546 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C6090547_ForceRegion_ParentChildForceRegions.py b/AutomatedTesting/Gem/PythonTests/physics/C6090547_ForceRegion_ParentChildForceRegions.py index a07fd1861b..84474f0226 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C6090547_ForceRegion_ParentChildForceRegions.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C6090547_ForceRegion_ParentChildForceRegions.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C6090547 # Test Case Title : Check that force regions in parent and child entities work together. -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/6090547 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C6090550_ForceRegion_WorldSpaceForceNegative.py b/AutomatedTesting/Gem/PythonTests/physics/C6090550_ForceRegion_WorldSpaceForceNegative.py index f8da62ebac..e2c55a024d 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C6090550_ForceRegion_WorldSpaceForceNegative.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C6090550_ForceRegion_WorldSpaceForceNegative.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ Test case ID : C6090550 Test Case Title : Check that force region exerts world space force on rigid bodies (negative test) -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/6090550 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C6090551_ForceRegion_LocalSpaceForceNegative.py b/AutomatedTesting/Gem/PythonTests/physics/C6090551_ForceRegion_LocalSpaceForceNegative.py index 0788b1da66..2e201e20bd 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C6090551_ForceRegion_LocalSpaceForceNegative.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C6090551_ForceRegion_LocalSpaceForceNegative.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ Test case ID : C6090551 Test Case Title : Check that force region exerts local space force on rigid bodies (negative test) -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/6090551 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C6090552_ForceRegion_LinearDampingNegative.py b/AutomatedTesting/Gem/PythonTests/physics/C6090552_ForceRegion_LinearDampingNegative.py index a878819045..585a337389 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C6090552_ForceRegion_LinearDampingNegative.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C6090552_ForceRegion_LinearDampingNegative.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ Test case ID : C6090552 Test Case Title : Check that force region exerts linear damping force on rigid bodies (negative test) -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/6090552 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C6090553_ForceRegion_SimpleDragForceOnRigidBodies.py b/AutomatedTesting/Gem/PythonTests/physics/C6090553_ForceRegion_SimpleDragForceOnRigidBodies.py index 43c4335918..6abee0ad64 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C6090553_ForceRegion_SimpleDragForceOnRigidBodies.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C6090553_ForceRegion_SimpleDragForceOnRigidBodies.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C6090553 # Test Case Title : Check that force region exerts simple drag force on rigid bodies (negative test) -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/6090553 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C6090554_ForceRegion_PointForceNegative.py b/AutomatedTesting/Gem/PythonTests/physics/C6090554_ForceRegion_PointForceNegative.py index d7781b6040..0969cebd8d 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C6090554_ForceRegion_PointForceNegative.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C6090554_ForceRegion_PointForceNegative.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ Test case ID : C6090554 Test Case Title : Check that force region exerts point force on rigid bodies (negative test) -URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/6090554 + """ diff --git a/AutomatedTesting/Gem/PythonTests/physics/C6090555_ForceRegion_SplineFollowOnRigidBodies.py b/AutomatedTesting/Gem/PythonTests/physics/C6090555_ForceRegion_SplineFollowOnRigidBodies.py index b1b76d896d..90f67391ad 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C6090555_ForceRegion_SplineFollowOnRigidBodies.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C6090555_ForceRegion_SplineFollowOnRigidBodies.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C6090555 # Test Case Title : Check that force region exerts spline follow force on rigid bodies(negative test) -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/6090555 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C6131473_StaticSlice_OnDynamicSliceSpawn.py b/AutomatedTesting/Gem/PythonTests/physics/C6131473_StaticSlice_OnDynamicSliceSpawn.py index 11281df4eb..ee68ef6049 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C6131473_StaticSlice_OnDynamicSliceSpawn.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C6131473_StaticSlice_OnDynamicSliceSpawn.py @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C6131473 # Test Case Title : Verify a static slice is not spawned automatically everytime a dynamic slice with # PhysX Components is spawned -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/6131473 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C6224408_ScriptCanvas_EntitySpawn.py b/AutomatedTesting/Gem/PythonTests/physics/C6224408_ScriptCanvas_EntitySpawn.py index 6501e11e68..6693c17dff 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C6224408_ScriptCanvas_EntitySpawn.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C6224408_ScriptCanvas_EntitySpawn.py @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C6224408 # Test Case Title : Entity using PhysX nodes in Script Canvas can be spawned. -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/6224408 + # fmt: off class Tests: diff --git a/AutomatedTesting/Gem/PythonTests/physics/C6274125_ScriptCanvas_TriggerEvents.py b/AutomatedTesting/Gem/PythonTests/physics/C6274125_ScriptCanvas_TriggerEvents.py index 44c8be9e1e..c360473b7f 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C6274125_ScriptCanvas_TriggerEvents.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C6274125_ScriptCanvas_TriggerEvents.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C6274125 # Test Case Title : Verify ScriptCanvas Trigger Events. -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/6274125 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/C6321601_Force_HighValuesDirectionAxes.py b/AutomatedTesting/Gem/PythonTests/physics/C6321601_Force_HighValuesDirectionAxes.py index c1a51f2887..4b0b036970 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/C6321601_Force_HighValuesDirectionAxes.py +++ b/AutomatedTesting/Gem/PythonTests/physics/C6321601_Force_HighValuesDirectionAxes.py @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # Test case ID : C6321601 # Test Case Title : Check that very high values of direction axes of forces do not throw error -# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/6321601 + # fmt: off diff --git a/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Main.py index f81445fe03..8f1f2f7481 100644 --- a/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Main.py @@ -73,7 +73,7 @@ class TestAutomation(TestAutomationBase): self._run_test(request, workspace, editor, test_module) @revert_physics_config - @fm.file_override('physxsystemconfiguration.setreg','C4982593_PhysXCollider_CollisionLayer.setreg', 'AutomatedTesting/Registry') + @fm.file_override('physxsystemconfiguration.setreg','C4982593_PhysXCollider_CollisionLayer.setreg_override', 'AutomatedTesting/Registry') def test_C4982593_PhysXCollider_CollisionLayerTest(self, request, workspace, editor, launcher_platform): from . import C4982593_PhysXCollider_CollisionLayerTest as test_module self._run_test(request, workspace, editor, test_module) diff --git a/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Periodic.py index 93831e9658..2b82c3e596 100755 --- a/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Periodic.py +++ b/AutomatedTesting/Gem/PythonTests/physics/TestSuite_Periodic.py @@ -308,19 +308,19 @@ class TestAutomation(TestAutomationBase): self._run_test(request, workspace, editor, test_module) @revert_physics_config - @fm.file_override('physxsystemconfiguration.setreg','C4976245_PhysXCollider_CollisionLayerTest.setreg', 'AutomatedTesting/Registry') + @fm.file_override('physxsystemconfiguration.setreg','C4976245_PhysXCollider_CollisionLayerTest.setreg_override', 'AutomatedTesting/Registry') def test_C4976245_PhysXCollider_CollisionLayerTest(self, request, workspace, editor, launcher_platform): from . import C4976245_PhysXCollider_CollisionLayerTest as test_module self._run_test(request, workspace, editor, test_module) @revert_physics_config - @fm.file_override('physxsystemconfiguration.setreg','C4976244_Collider_SameGroupSameLayerCollision.setreg', 'AutomatedTesting/Registry') + @fm.file_override('physxsystemconfiguration.setreg','C4976244_Collider_SameGroupSameLayerCollision.setreg_override', 'AutomatedTesting/Registry') def test_C4976244_Collider_SameGroupSameLayerCollision(self, request, workspace, editor, launcher_platform): from . import C4976244_Collider_SameGroupSameLayerCollision as test_module self._run_test(request, workspace, editor, test_module) @revert_physics_config - @fm.file_override('physxdefaultsceneconfiguration.setreg','C14195074_ScriptCanvas_PostUpdateEvent.setreg', 'AutomatedTesting/Registry') + @fm.file_override('physxdefaultsceneconfiguration.setreg','C14195074_ScriptCanvas_PostUpdateEvent.setreg_override', 'AutomatedTesting/Registry') def test_C14195074_ScriptCanvas_PostUpdateEvent(self, request, workspace, editor, launcher_platform): from . import C14195074_ScriptCanvas_PostUpdateEvent as test_module self._run_test(request, workspace, editor, test_module) @@ -331,7 +331,7 @@ class TestAutomation(TestAutomationBase): self._run_test(request, workspace, editor, test_module) @revert_physics_config - @fm.file_override('physxdefaultsceneconfiguration.setreg','C14902097_ScriptCanvas_PreUpdateEvent.setreg', 'AutomatedTesting/Registry') + @fm.file_override('physxdefaultsceneconfiguration.setreg','C14902097_ScriptCanvas_PreUpdateEvent.setreg_override', 'AutomatedTesting/Registry') def test_C14902097_ScriptCanvas_PreUpdateEvent(self, request, workspace, editor, launcher_platform): from . import C14902097_ScriptCanvas_PreUpdateEvent as test_module self._run_test(request, workspace, editor, test_module) @@ -365,7 +365,7 @@ class TestAutomation(TestAutomationBase): self._run_test(request, workspace, editor, test_module) @revert_physics_config - @fm.file_override('physxsystemconfiguration.setreg','C4976227_Collider_NewGroup.setreg', 'AutomatedTesting/Registry') + @fm.file_override('physxsystemconfiguration.setreg','C4976227_Collider_NewGroup.setreg_override', 'AutomatedTesting/Registry') def test_C4976227_Collider_NewGroup(self, request, workspace, editor, launcher_platform): from . import C4976227_Collider_NewGroup as test_module self._run_test(request, workspace, editor, test_module) @@ -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) @@ -450,7 +452,7 @@ class TestAutomation(TestAutomationBase): self._run_test(request, workspace, editor, test_module) @revert_physics_config - @fm.file_override('physxsystemconfiguration.setreg','C3510644_Collider_CollisionGroups.setreg', 'AutomatedTesting/Registry') + @fm.file_override('physxsystemconfiguration.setreg','C3510644_Collider_CollisionGroups.setreg_override', 'AutomatedTesting/Registry') def test_C3510644_Collider_CollisionGroups(self, request, workspace, editor, launcher_platform): from . import C3510644_Collider_CollisionGroups as test_module self._run_test(request, workspace, editor, test_module) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/AssetEditor_CreateScriptEventFile.py b/AutomatedTesting/Gem/PythonTests/scripting/AssetEditor_CreateScriptEventFile.py new file mode 100644 index 0000000000..dcbbf47f0c --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/AssetEditor_CreateScriptEventFile.py @@ -0,0 +1,124 @@ +""" +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 ID: T92569013 +Test Case Title: Script Event file can be created +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92569013 +""" + + +# fmt: off +class Tests(): + new_event_created = ("New Script Event created", "New Script Event not created") + child_event_created = ("Child Event created", "Child not created") + file_saved = ("Script event file saved", "Script event file did not save") + console_error = ("No unexpected error in console", "Error found in console") + console_warning = ("No unexpected warning in console", "Warning found in console") +# fmt: on + + +def CreateScriptEventFile(): + """ + Summary: + Script Event file can be created + + Expected Behavior: + File is created without any errors and warnings in Console + + Test Steps: + 1) Open Asset Editor + 2) Get Asset Editor Qt object + 3) Create new Script Event Asset + 4) Add new child event + 5) Save the Script Event file + 6) Verify if file is created + 7) Verify console for errors/warnings + 8) Close Asset Editor + + 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 utils import Report + from utils import TestHelper as helper + from utils import Tracer + import pyside_utils + + # Open 3D Engine imports + import azlmbr.legacy.general as general + import azlmbr.editor as editor + import azlmbr.bus as bus + + # Pyside imports + from PySide2 import QtWidgets + + GENERAL_WAIT = 1.0 # seconds + + FILE_PATH = os.path.join("AutomatedTesting", "ScriptCanvas", "test_file.scriptevent") + + # 1) Open Asset Editor + general.idle_enable(True) + # Initially close the Asset Editor and then reopen to ensure we don't have any existing assets open + general.close_pane("Asset Editor") + general.open_pane("Asset Editor") + helper.wait_for_condition(lambda: general.is_pane_visible("Asset Editor"), 5.0) + + # 2) Get Asset Editor Qt object + editor_window = pyside_utils.get_editor_main_window() + asset_editor_widget = editor_window.findChild(QtWidgets.QDockWidget, "Asset Editor").findChild( + QtWidgets.QWidget, "AssetEditorWindowClass" + ) + container = asset_editor_widget.findChild(QtWidgets.QWidget, "ContainerForRows") + menu_bar = asset_editor_widget.findChild(QtWidgets.QMenuBar) + + # 3) Create new Script Event Asset + action = pyside_utils.find_child_by_pattern(menu_bar, {"type": QtWidgets.QAction, "text": "Script Events"}) + action.trigger() + result = helper.wait_for_condition( + lambda: container.findChild(QtWidgets.QFrame, "Events") is not None, 3 * GENERAL_WAIT + ) + Report.result(Tests.new_event_created, result) + + # 4) Add new child event + add_event = container.findChild(QtWidgets.QFrame, "Events").findChild(QtWidgets.QToolButton, "") + add_event.click() + result = helper.wait_for_condition( + lambda: asset_editor_widget.findChild(QtWidgets.QFrame, "EventName") is not None, GENERAL_WAIT + ) + Report.result(Tests.child_event_created, result) + + with Tracer() as section_tracer: + # 5) Save the Script Event file + editor.AssetEditorWidgetRequestsBus(bus.Broadcast, "SaveAssetAs", FILE_PATH) + + # 6) Verify if file is created + result = helper.wait_for_condition(lambda: os.path.exists(FILE_PATH), 3 * GENERAL_WAIT) + Report.result(Tests.file_saved, result) + + # 7) Verify console for errors/warnings + Report.result(Tests.console_error, not section_tracer.has_errors) + Report.result(Tests.console_warning, not section_tracer.has_warnings) + + # 8) Close Asset Editor + general.close_pane("Asset Editor") + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + + from utils import Report + + Report.start_test(CreateScriptEventFile) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/AssetEditor_NewScriptEvent.py b/AutomatedTesting/Gem/PythonTests/scripting/AssetEditor_NewScriptEvent.py new file mode 100644 index 0000000000..1f801d4eeb --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/AssetEditor_NewScriptEvent.py @@ -0,0 +1,129 @@ +""" +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 ID: T92568942 +Test Case Title: Clicking the "+" button and selecting "New Script Event" opens the +Asset Editor with a new Script Event asset +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92568942 +""" + +from PySide2 import QtWidgets +import azlmbr.legacy.general as general + +import editor_python_test_tools.pyside_utils as pyside_utils +from editor_python_test_tools.utils import TestHelper as helper +from editor_python_test_tools.utils import Report + + +class Tests: + action_found = "New Script event action found" + asset_editor_opened = "Asset Editor opened" + new_asset = "Asset Editor created with new asset" + script_event = "New Script event created in Asset Editor" + + +GENERAL_WAIT = 0.5 # seconds + + +class TestAssetEditor_NewScriptEvent: + """ + Summary: + Clicking the "+" button in Node Palette and creating New Script Event opens Asset Editor + + Expected Behavior: + Clicking the "+" button and selecting "New Script Event" opens the Asset Editor with a + new Script Event asset + + Test Steps: + 1) Open Script Canvas window (Tools > Script Canvas) + 2) Close any existing AssetEditor window + 3) Get the SC window object + 4) Click on New Script Event on Node palette + 5) Verify if Asset Editor opened + 6) Verify if a new asset with Script Canvas category is opened + 7) Close Script Canvas and Asset Editor + + + 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 + """ + + @pyside_utils.wrap_async + async def run_test(self): + # 1) Open Script Canvas window (Tools > Script Canvas) + general.idle_enable(True) + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) + + # 2) Close any existing AssetEditor window + general.close_pane("Asset Editor") + helper.wait_for_condition(lambda: not general.is_pane_visible("Asset Editor"), 5.0) + + # 3) Get the SC window object + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + node_palette = sc.findChild(QtWidgets.QDockWidget, "NodePalette") + frame = node_palette.findChild(QtWidgets.QFrame, "searchCustomization") + button = frame.findChild(QtWidgets.QToolButton) + pyside_utils.click_button_async(button) + + # 4) Click on New Script Event on Node palette + menu = None + + def menu_has_focus(): + nonlocal menu + for fw in [ + QtWidgets.QApplication.activePopupWidget(), + QtWidgets.QApplication.activeModalWidget(), + QtWidgets.QApplication.focusWidget(), + QtWidgets.QApplication.activeWindow(), + ]: + print(fw) + if fw and isinstance(fw, QtWidgets.QMenu) and fw.isVisible(): + menu = fw + return True + return False + + await pyside_utils.wait_for_condition(menu_has_focus, GENERAL_WAIT) + action = await pyside_utils.wait_for_action_in_menu(menu, {"text": "New Script Event"}) + Report.info(f"{Tests.action_found}: {action is not None}") + action.trigger() + pyside_utils.queue_hide_event(menu) + + # 5) Verify if Asset Editor opened + result = helper.wait_for_condition(lambda: general.is_pane_visible("Asset Editor"), GENERAL_WAIT) + Report.info(f"{Tests.asset_editor_opened}: {result}") + + # 6) Verify if a new asset with Script Canvas category is opened + asset_editor = editor_window.findChild(QtWidgets.QDockWidget, "Asset Editor") + row_container = asset_editor.findChild(QtWidgets.QWidget, "ContainerForRows") + # NOTE: QWidget ContainerForRows will have frames of Name, Category, ToolTip etc. + # To validate if a new script event file is generated, we check for + # QFrame Category and its value + categories = row_container.findChildren(QtWidgets.QFrame, "Category") + Report.info(f"{Tests.new_asset}: {len(categories)>0}") + result = False + for frame in categories: + line_edit = frame.findChild(QtWidgets.QLineEdit) + result = True if (line_edit and line_edit.text() == "Script Events") else False + Report.info(f"{Tests.script_event}: {result}") + + # 7) Close Script Canvas and Asset Editor + general.close_pane("Script Canvas") + general.close_pane("Asset Editor") + + +test = TestAssetEditor_NewScriptEvent() +test.run_test() diff --git a/AutomatedTesting/Gem/PythonTests/scripting/Debugging_TargetMultipleEntities.py b/AutomatedTesting/Gem/PythonTests/scripting/Debugging_TargetMultipleEntities.py new file mode 100644 index 0000000000..a26cb4f923 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/Debugging_TargetMultipleEntities.py @@ -0,0 +1,144 @@ +""" +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 ID: T92568856 +Test Case Title: Multiple Entities can be targeted in the Debugger tool +URLs of the test case: https://testrail.agscollab.com/index.php?/tests/view/92568856 +""" + + +# fmt: off +class Tests(): + level_created = ("New level created", "New level not created") + entities_found = ("Entities are found in Logging window", "Entities are not found in Logging window") + select_multiple_targets = ("Multiple targets are selected", "Multiple targets are not selected") +# fmt: on + + +GENERAL_WAIT = 0.5 # seconds + + +def Debugging_TargetMultipleEntities(): + """ + Summary: + Multiple Entities can be targeted in the Debugger tool + + Expected Behavior: + Selected files can be checked for logging. + Upon checking, checkboxes of the parent folders change to either full or partial check. + + Test Steps: + 1) Create temp level + 2) Create two entities with scriptcanvas components + 3) Set values for scriptcanvas + 4) Open Script Canvas window and get sc opbject + 5) Open Debugging(Logging) window + 6) Click on Entities tab in logging window + 7) Verify if the scriptcanvas exist under entities + 8) Verify if the entities can be selected + 9) Close Debugging window and Script Canvas window + + + 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 + """ + + from PySide2 import QtWidgets + from PySide2.QtCore import Qt + import azlmbr.legacy.general as general + import azlmbr.math as math + import azlmbr.asset as asset + import azlmbr.bus as bus + + import os + import pyside_utils + import hydra_editor_utils as hydra + from utils import TestHelper as helper + from utils import Report + + LEVEL_NAME = "tmp_level" + ASSET_NAME_1 = "ScriptCanvas_TwoComponents0.scriptcanvas" + ASSET_NAME_2 = "ScriptCanvas_TwoComponents1.scriptcanvas" + ASSET_1 = os.path.join("scriptcanvas", ASSET_NAME_1) + ASSET_2 = os.path.join("scriptcanvas", ASSET_NAME_2) + WAIT_TIME = 3.0 + + def get_asset(asset_path): + return asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False) + + # 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 two entities with scriptcanvas components + position = math.Vector3(512.0, 512.0, 32.0) + test_entity_1 = hydra.Entity("test_entity_1") + test_entity_1.create_entity(position, ["Script Canvas"]) + test_entity_2 = hydra.Entity("test_entity_2") + test_entity_2.create_entity(position, ["Script Canvas"]) + + # 3) Set values for scriptcanvas + test_entity_1.get_set_test(0, "Script Canvas Asset|Script Canvas Asset", get_asset(ASSET_1)) + test_entity_2.get_set_test(0, "Script Canvas Asset|Script Canvas Asset", get_asset(ASSET_2)) + + # 4) Open Script Canvas window and get sc opbject + general.open_pane("Script Canvas") + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + + # 5) Open Debugging(Logging) window + if ( + sc.findChild(QtWidgets.QDockWidget, "LoggingWindow") is None + or not sc.findChild(QtWidgets.QDockWidget, "LoggingWindow").isVisible() + ): + action = pyside_utils.find_child_by_pattern(sc, {"text": "Debugging", "type": QtWidgets.QAction}) + action.trigger() + logging_window = sc.findChild(QtWidgets.QDockWidget, "LoggingWindow") + + # 6) Click on Entities tab in logging window + button = pyside_utils.find_child_by_pattern(logging_window, {"type": QtWidgets.QPushButton, "text": "Entities"}) + button.click() + + # 7) Verify if the scriptcanvas exist under entities + entities = logging_window.findChild(QtWidgets.QWidget, "entitiesPage") + tree = entities.findChild(QtWidgets.QTreeView, "pivotTreeView") + asset_1_mi = pyside_utils.find_child_by_pattern(tree, ASSET_NAME_1.lower()) + asset_2_mi = pyside_utils.find_child_by_pattern(tree, ASSET_NAME_2.lower()) + result = asset_1_mi is not None and asset_2_mi is not None + Report.critical_result(Tests.entities_found, result) + + # 8) Verify if the entities can be selected + tree.expandAll() + tree.model().setData(asset_1_mi, 2, Qt.CheckStateRole) + tree.model().setData(asset_2_mi, 2, Qt.CheckStateRole) + checklist = [asset_1_mi, asset_1_mi.parent(), asset_2_mi, asset_2_mi.parent()] + result = all([index.data(Qt.CheckStateRole) == 2 for index in checklist]) + Report.critical_result(Tests.select_multiple_targets, result) + + # 9) Close Debugging window and Script Canvas window + logging_window.close() + general.close_pane("Script Canvas") + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + from utils import Report + + Report.start_test(Debugging_TargetMultipleEntities) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/Debugging_TargetMultipleGraphs.py b/AutomatedTesting/Gem/PythonTests/scripting/Debugging_TargetMultipleGraphs.py new file mode 100644 index 0000000000..4aa5c822a7 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/Debugging_TargetMultipleGraphs.py @@ -0,0 +1,113 @@ +""" +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 ID: T92569137 +Test Case Title: Multiple Graphs can be targeted in the Debugger tool +URLs of the test case: https://testrail.agscollab.com/index.php?/tests/view/92569137 +""" + + +# fmt: off +class Tests(): + select_multiple_targets = ("Multiple targets are selected", "Multiple targets are not selected") +# fmt: on + + +GENERAL_WAIT = 0.5 # seconds + + +def Debugging_TargetMultipleGraphs(): + """ + Summary: + Multiple Graphs can be targeted in the Debugger tool + + Expected Behavior: + Selected files can be checked for logging. + Upon checking, checkboxes of the parent folders change to either full or partial check. + + Test Steps: + 1) Open Script Canvas window (Tools > Script Canvas) + 2) Get the SC window object + 3) Open Debugging Tool if not opened already + 4) Select Graphs tab under logging window + 5) Select multiple targets from levels and scriptcanvas + 6) Verify if multiple targets are selected + 7) Close Debugging window and Script Canvas window + + + 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 + """ + + from PySide2 import QtWidgets + from PySide2.QtCore import Qt + import azlmbr.legacy.general as general + + import pyside_utils + from utils import TestHelper as helper + from utils import Report + + # 1) Open Script Canvas window (Tools > Script Canvas) + general.idle_enable(True) + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 6.0) + + # 2) Get the SC window object + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + + # 3) Open Debugging Tool if not opened already + if ( + sc.findChild(QtWidgets.QDockWidget, "LoggingWindow") is None + or not sc.findChild(QtWidgets.QDockWidget, "LoggingWindow").isVisible() + ): + action = pyside_utils.find_child_by_pattern(sc, {"text": "Debugging", "type": QtWidgets.QAction}) + action.trigger() + logging_window = sc.findChild(QtWidgets.QDockWidget, "LoggingWindow") + + # 4) Select Graphs tab under logging window + button = pyside_utils.find_child_by_pattern(logging_window, {"type": QtWidgets.QPushButton, "text": "Graphs"}) + button.click() + + # 5) Select multiple targets from levels and scriptcanvas + graphs = logging_window.findChild(QtWidgets.QWidget, "graphsPage") + tree = graphs.findChild(QtWidgets.QTreeView, "pivotTreeView") + # Select the first child under levels + level_model_index = pyside_utils.find_child_by_pattern(tree, "levels") + level_child_index = pyside_utils.get_item_view_index(tree, 0, 0, level_model_index) + tree.model().setData(level_child_index, 2, Qt.CheckStateRole) + # Select the first child under scriptcanvas + sc_model_index = pyside_utils.find_child_by_pattern(tree, "scriptcanvas") + sc_child_index = pyside_utils.get_item_view_index(tree, 0, 0, sc_model_index) + tree.model().setData(sc_child_index, 2, Qt.CheckStateRole) + + # 6) Verify if multiple targets are selected + result = all([index.data(Qt.CheckStateRole) != 0 for index in (level_model_index, sc_model_index)]) + result = result and all([index.data(Qt.CheckStateRole) == 2 for index in (level_child_index, sc_child_index)]) + Report.result(Tests.select_multiple_targets, result) + + # 7) Close Debugging window and Script Canvas window + logging_window.close() + general.close_pane("Script Canvas") + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + + from utils import Report + + Report.start_test(Debugging_TargetMultipleGraphs) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/Docking_Pane.py b/AutomatedTesting/Gem/PythonTests/scripting/Docking_Pane.py index 25dd83e5e2..4fa1e1257f 100755 --- a/AutomatedTesting/Gem/PythonTests/scripting/Docking_Pane.py +++ b/AutomatedTesting/Gem/PythonTests/scripting/Docking_Pane.py @@ -16,7 +16,6 @@ URLs of the test case: https://testrail.agscollab.com/index.php?/cases/view/1702 # fmt: off class Tests(): - open_sc_window = ("Script Canvas window is opened", "Failed to open Script Canvas window") pane_opened = ("Pane is opened successfully", "Failed to open pane") dock_pane = ("Pane is docked successfully", "Failed to dock Pane into one or more allowed area") # fmt: on @@ -78,8 +77,7 @@ def Docking_Pane(): # 1) Open Script Canvas window (Tools > Script Canvas) general.open_pane("Script Canvas") - is_sc_visible = helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) - Report.result(Tests.open_sc_window, is_sc_visible) + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) # 2) Make sure Node Palette pane is opened editor_window = pyside_utils.get_editor_main_window() diff --git a/AutomatedTesting/Gem/PythonTests/scripting/EditMenu_UndoRedo.py b/AutomatedTesting/Gem/PythonTests/scripting/EditMenu_UndoRedo.py new file mode 100644 index 0000000000..a87d9e9be9 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/EditMenu_UndoRedo.py @@ -0,0 +1,123 @@ +""" +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 ID: T92569049 +Test Case Title: Edit > Undo undoes the last action +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92569049 +Test case ID: T92569051 +Test Case Title: Edit > Redo redoes the last undone action +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92569051 +""" + + +# fmt: off +class Tests(): + variable_created = ("New variable created", "New variable not created") + undo_worked = ("Undo action working", "Undo action did not work") + redo_worked = ("Redo action working", "Redo action did not work") +# fmt: on + + +def EditMenu_UndoRedo(): + """ + Summary: + Edit > Undo undoes the last action + Edit > Redo redoes the last undone action + We create a new variable in variable manager, undo and verify if variable is removed, + redo it and verify if the variable is created again. + + Expected Behavior: + The last action is undone. + The last undone action is redone. + + Test Steps: + 1) Open Script Canvas window (Tools > Script Canvas) + 2) Get the SC window object + 3) Open Variable Manager if not opened already + 4) Create Graph + 5) Create new variable + 6) Verify if the variable is created initially + 7) Trigger Undo action and verify if variable is removed in Variable Manager + 8) Trigger Redo action and verify if variable is readded in Variable Manager + 9) Close SC window + + 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 + """ + + from PySide2 import QtWidgets, QtCore + + import azlmbr.legacy.general as general + + import pyside_utils + + # 1) Open Script Canvas window + general.idle_enable(True) + general.open_pane("Script Canvas") + + # 2) Get the SC window object + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + + # 3) Open Variable Manager if not opened already + if sc.findChild(QtWidgets.QDockWidget, "VariableManager") is None: + action = pyside_utils.find_child_by_pattern(sc, {"text": "Variable Manager", "type": QtWidgets.QAction}) + action.trigger() + variable_manager = sc.findChild(QtWidgets.QDockWidget, "VariableManager") + + # 4) Create Graph + action = pyside_utils.find_child_by_pattern(sc, {"objectName": "action_New_Script", "type": QtWidgets.QAction}) + action.trigger() + + # 5) Create new variable + add_button = variable_manager.findChild(QtWidgets.QPushButton, "addButton") + add_button.click() # Click on Create Variable button + # Select variable type + table_view = variable_manager.findChild(QtWidgets.QTableView, "variablePalette") + model_index = pyside_utils.find_child_by_pattern(table_view, "Boolean") + # Click on it to create variable + pyside_utils.item_view_index_mouse_click(table_view, model_index) + + # 6) Verify if the variable is created initially + graph_vars = variable_manager.findChild(QtWidgets.QTableView, "graphVariables") + result = graph_vars.model().rowCount(QtCore.QModelIndex()) == 1 # since we added 1 variable, rowcount=1 + Report.result(Tests.variable_created, result) + + # 7) Trigger Undo action and verify if variable is removed in Variable Manager + action = sc.findChild(QtWidgets.QAction, "action_Undo") + action.trigger() + result = graph_vars.model().rowCount(QtCore.QModelIndex()) == 0 # since we triggered undo, rowcount=0 + Report.result(Tests.undo_worked, result) + + # 8) Trigger Redo action and verify if variable is readded in Variable Manager + action = sc.findChild(QtWidgets.QAction, "action_Redo") + action.trigger() + result = ( + graph_vars.model().rowCount(QtCore.QModelIndex()) == 1 + ) # since action is redone 1 variable is readded, rowcount=1 + Report.result(Tests.redo_worked, result) + + # 9) Close SC window + general.close_pane("Script Canvas") + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + + from utils import Report + + Report.start_test(EditMenu_UndoRedo) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/Entity_AddScriptCanvasComponent.py b/AutomatedTesting/Gem/PythonTests/scripting/Entity_AddScriptCanvasComponent.py new file mode 100644 index 0000000000..fd8e9b1173 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/Entity_AddScriptCanvasComponent.py @@ -0,0 +1,88 @@ +""" +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 ID: T92562978 +Test Case Title: Script Canvas Component can be added to an entity +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92562978 +""" + + +# fmt: off +class Tests(): + level_created = ("New level created", "Failed to create new level") + entity_created = ("Test Entity created", "Failed to create test entity") + add_sc_component = ("Script Canvas component added to entity", "Failed to add SC component to entity") + no_errors_found = ("Tracer found no errors", "One or more errors found by Tracer") + no_warnings_found = ("Tracer found no warnings", "One or more warnings found by Tracer") +# fmt: on + + +def Entity_AddScriptCanvasComponent(): + """ + Summary: + verify if Script Canvas component can be added to Entity without any issue + + Expected Behavior: + Script Canvas Component is added to the entity successfully without issue. + + Test Steps: + 1) Create temp level + 2) Create test entity + 3) Start Tracer + 4) Add Script Canvas component to test entity + 5) Search for errors and warnings + + + 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 + """ + + from utils import TestHelper as helper + from utils import Tracer + from editor_entity_utils import EditorEntity + import azlmbr.legacy.general as general + + LEVEL_NAME = "tmp_level" + WAIT_TIME = 3.0 # SECONDS + + # 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 new entity + test_entity = EditorEntity.create_editor_entity("test_entity") + Report.result(Tests.entity_created, test_entity.id.IsValid()) + + # 3) Start Tracer + with Tracer() as section_tracer: + + # 4) Add Script Canvas component to test entity + test_entity.add_component("Script Canvas") + Report.result(Tests.add_sc_component, test_entity.has_component("Script Canvas")) + + # 5) Search for errors and warnings + Report.result(Tests.no_errors_found, not section_tracer.has_errors) + Report.result(Tests.no_warnings_found, not section_tracer.has_warnings) + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + from utils import Report + + Report.start_test(Entity_AddScriptCanvasComponent) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/FileMenu_New_Open.py b/AutomatedTesting/Gem/PythonTests/scripting/FileMenu_New_Open.py new file mode 100644 index 0000000000..f72ac8ea01 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/FileMenu_New_Open.py @@ -0,0 +1,98 @@ +""" +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 ID: T92569037 +Test Case Title: File > New Script creates a new script +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92569037 +Test case ID: T92569039 +Test Case Title: File > Open opens the Open... dialog +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92569039 +""" + +import os +import sys +from PySide2 import QtWidgets +import azlmbr.legacy.general as general + +import editor_python_test_tools.pyside_utils as pyside_utils +from editor_python_test_tools.utils import Report + +# fmt: off +class Tests(): + new_action = "File->New action working as expected" + open_action = "File->Open action working as expected" +# fmt: on + + +GENERAL_WAIT = 0.5 # seconds + + +class TestFileMenuNewOpen: + """ + Summary: + When clicked on File->New, new script opens and File->Open should open the FileBrowser + + Expected Behavior: + New and Open actions should work as expected. + + Test Steps: + 1) Open Script Canvas window (Tools > Script Canvas) + 2) Get the SC window object + 3) Trigger File->New action + 4) Verify if New tab is opened + 5) Trigger File->Open action + 6) Close Script Canvas window + + + 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 + """ + + @pyside_utils.wrap_async + async def run_test(self): + # 1) Open Script Canvas window (Tools > Script Canvas) + general.open_pane("Script Canvas") + + # 2) Get the SC window object + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + sc_main = sc.findChild(QtWidgets.QMainWindow) + sc_tabs = sc_main.findChild(QtWidgets.QTabWidget, "ScriptCanvasTabs") + + # 3) Trigger File->New action + initial_tabs_count = sc_tabs.count() + action = pyside_utils.find_child_by_pattern( + sc_main, {"objectName": "action_New_Script", "type": QtWidgets.QAction} + ) + action.trigger() + + # 4) Verify if New tab is opened + general.idle_wait(GENERAL_WAIT) + Report.info(f"{Tests.new_action}: {sc_tabs.count() == initial_tabs_count + 1}") + + # 5) Trigger File->Open action + action = pyside_utils.find_child_by_pattern(sc_main, {"objectName": "action_Open", "type": QtWidgets.QAction}) + pyside_utils.trigger_action_async(action) + general.idle_wait(GENERAL_WAIT) + popup = await pyside_utils.wait_for_modal_widget() + Report.info(f"{Tests.open_action}: {popup and 'Open' in popup.windowTitle()}") + popup.close() + + # 6) Close Script Canvas window + general.close_pane("Script Canvas") + + +test = TestFileMenuNewOpen() +test.run_test() diff --git a/AutomatedTesting/Gem/PythonTests/scripting/GraphClose_SavePrompt.py b/AutomatedTesting/Gem/PythonTests/scripting/GraphClose_SavePrompt.py new file mode 100644 index 0000000000..ce610b159b --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/GraphClose_SavePrompt.py @@ -0,0 +1,110 @@ +""" +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 ID: T92563070 +Test Case Title: Graphs can be closed by clicking X on the Graph name tab +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92563070 +Test case ID: T92563068 +Test Case Title: Save Prompt: User is prompted to save a graph on close after +creating a new graph +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92563068 +""" + +import os +import sys +from PySide2 import QtWidgets +import azlmbr.legacy.general as general + +import editor_python_test_tools.pyside_utils as pyside_utils +from editor_python_test_tools.utils import TestHelper as helper +from editor_python_test_tools.utils import Report + +# fmt: off +class Tests(): + new_graph = "New graph created" + save_prompt = "Save prompt opened as expected" + close_graph = "Close button worked as expected" +# fmt: on + + +GENERAL_WAIT = 0.5 # seconds + + +class TestGraphCloseSavePrompt: + """ + Summary: + The graph is closed when x button is clicked. + Save Prompt is opened before closing. + + Expected Behavior: + New and Open actions should work as expected. + + Test Steps: + 1) Open Script Canvas window (Tools > Script Canvas) + 2) Get the SC window object + 3) Trigger File->New action + 4) Verify if New tab is opened + 5) Close new tab using X on top of graph and check for save dialog + 6) Check if tab is closed + 7) Close Script Canvas window + + 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 + """ + + @pyside_utils.wrap_async + async def run_test(self): + # 1) Open Script Canvas window (Tools > Script Canvas) + general.idle_enable(True) + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) + + # 2) Get the SC window object + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + sc_main = sc.findChild(QtWidgets.QMainWindow) + sc_tabs = sc_main.findChild(QtWidgets.QTabWidget, "ScriptCanvasTabs") + tab_bar = sc_tabs.findChild(QtWidgets.QTabBar) + + # 3) Trigger File->New action + initial_tabs_count = sc_tabs.count() + action = pyside_utils.find_child_by_pattern( + sc_main, {"objectName": "action_New_Script", "type": QtWidgets.QAction} + ) + action.trigger() + + # 4) Verify if New tab is opened + result = helper.wait_for_condition(lambda: sc_tabs.count() == initial_tabs_count + 1, GENERAL_WAIT) + Report.info(f"{Tests.new_graph}: {result}") + + # 5) Close new tab using X on top of graph and check for save dialog + close_button = tab_bar.findChildren(QtWidgets.QAbstractButton)[0] + pyside_utils.click_button_async(close_button) + popup = await pyside_utils.wait_for_modal_widget() + if popup: + Report.info(f"{Tests.save_prompt}: {popup.findChild(QtWidgets.QDialog, 'SaveChangesDialog') is not None}") + dont_save = popup.findChild(QtWidgets.QPushButton, "m_continueButton") + dont_save.click() + + # 6) Check if tab is closed + await pyside_utils.wait_for_condition(lambda: sc_tabs.count() == initial_tabs_count, 5.0) + Report.info(f"{Tests.close_graph}: {sc_tabs.count()==initial_tabs_count}") + + # 7) Close Script Canvas window + general.close_pane("Script Canvas") + + +test = TestGraphCloseSavePrompt() +test.run_test() diff --git a/AutomatedTesting/Gem/PythonTests/scripting/Graph_ZoomInZoomOut.py b/AutomatedTesting/Gem/PythonTests/scripting/Graph_ZoomInZoomOut.py new file mode 100644 index 0000000000..a93b6e61d1 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/Graph_ZoomInZoomOut.py @@ -0,0 +1,120 @@ +""" +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 ID: T92569079 +Test Case Title: View > Zoom In zooms the graph in +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92569079 +Test case ID: T92569081 +Test Case Title: View > Zoom In zooms the graph out +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92569081 +""" + + +# fmt: off +class Tests(): + zoom_in = ("Zoom In action working as expected", "Zoom In action not working as expected") + zoom_out = ("Zoom Out action working as expected", "Zoom Out action not working as expected") +# fmt: on + + +GENERAL_WAIT = 0.5 # seconds + + +def Graph_ZoomInZoomOut(): + """ + Summary: + The graph can be zoomed in and zoomed out. + + Expected Behavior: + The graph is zoomed in when when we click View->ZoomIn + The graph is zoomed out when when we click View->ZoomOut + + Test Steps: + 1) Open Script Canvas window (Tools > Script Canvas) + 2) Get the SC window object + 3) Create new graph + 4) Get initial graph transform values + 5) Trigger Zoom In and verify if the graph transform scale is increased + 6) Trigger Zoom Out and verify if the graph transform scale is decreased + 7) Close Script Canvas window + + + 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 + """ + + # Helper imports + import ImportPathHelper as imports + + imports.init() + + from PySide2 import QtWidgets + import azlmbr.legacy.general as general + + import pyside_utils + from utils import TestHelper as helper + from utils import Report + + # 1) Open Script Canvas window (Tools > Script Canvas) + general.idle_enable(True) + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) + + # 2) Get the SC window object + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + sc_main = sc.findChild(QtWidgets.QMainWindow) + + # 3) Create new graph + create_new_graph = pyside_utils.find_child_by_pattern( + sc_main, {"objectName": "action_New_Script", "type": QtWidgets.QAction} + ) + create_new_graph.trigger() + + # 4) Get initial graph transform values + graphics_view = sc_main.findChild(QtWidgets.QGraphicsView) + # NOTE: transform m11 and m22 are horizontal and vertical scales of graph + # they increase when zoomed in and decreased when zoomed out + curr_m11, curr_m22 = graphics_view.transform().m11(), graphics_view.transform().m22() + + # 5) Trigger Zoom In and verify if the graph transform scale is increased + zin = pyside_utils.find_child_by_pattern(sc_main, {"objectName": "action_ZoomIn", "type": QtWidgets.QAction}) + zin.trigger() + result = helper.wait_for_condition( + lambda: curr_m11 < graphics_view.transform().m11() and curr_m22 < graphics_view.transform().m22(), GENERAL_WAIT, + ) + Report.result(Tests.zoom_in, result) + + # 6) Trigger Zoom Out and verify if the graph transform scale is decreased + curr_m11, curr_m22 = graphics_view.transform().m11(), graphics_view.transform().m22() + zout = pyside_utils.find_child_by_pattern(sc_main, {"objectName": "action_ZoomOut", "type": QtWidgets.QAction}) + zout.trigger() + result = helper.wait_for_condition( + lambda: curr_m11 > graphics_view.transform().m11() and curr_m22 > graphics_view.transform().m22(), GENERAL_WAIT, + ) + Report.result(Tests.zoom_out, result) + + # 7) Close Script Canvas window + general.close_pane("Script Canvas") + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + + from utils import Report + + Report.start_test(Graph_ZoomInZoomOut) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/ImportPathHelper.py b/AutomatedTesting/Gem/PythonTests/scripting/ImportPathHelper.py index 8aede24d0e..a45024cebf 100755 --- a/AutomatedTesting/Gem/PythonTests/scripting/ImportPathHelper.py +++ b/AutomatedTesting/Gem/PythonTests/scripting/ImportPathHelper.py @@ -13,4 +13,5 @@ def init(): import os import sys sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared') + sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../EditorPythonTestTools/editor_python_test_tools') \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonTests/scripting/NodeInspector_RenameVariable.py b/AutomatedTesting/Gem/PythonTests/scripting/NodeInspector_RenameVariable.py new file mode 100644 index 0000000000..33d3f4137a --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/NodeInspector_RenameVariable.py @@ -0,0 +1,132 @@ +""" +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 ID: T92568982 +Test Case Title: Renaming variables in the Node Inspector +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92568982 +""" + + +# fmt: off +class Tests(): + variable_created = ("New variable created", "New variable is not created") + node_inspector_rename = ("Variable is renamed in Node Inspector", "Variable is not renamed in Node Inspector") + variable_manager_rename = ("Variable is renamed in Variable Manager", "Variable is not renamed in Variable Manager") +# fmt: on + + +GENERAL_WAIT = 0.5 # seconds + + +def NodeInspector_RenameVariable(): + """ + Summary: + Renaming variables in the Node Inspector, renames the actual variable. + + Expected Behavior: + The Variable's name is changed in both Node Inspector and Variable Manager. + + Test Steps: + 1) Open Script Canvas window (Tools > Script Canvas) + 2) Get the SC window object + 3) Open Variable Manager if not opened already + 4) Open Node Inspector if not opened already + 5) Create new graph and a new variable in Variable manager + 6) Click on the variable + 7) Update name in Node Inspector and click on ENTER + 8) Verify if the name is updated in Node inspector and Variable manager + 9) Close Script Canvas window + + 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 + """ + + TEST_NAME = "test name" + + from PySide2 import QtWidgets, QtCore, QtTest + from PySide2.QtCore import Qt + import azlmbr.legacy.general as general + + import pyside_utils + from utils import TestHelper as helper + + def open_tool(sc, dock_widget_name, pane_name): + if sc.findChild(QtWidgets.QDockWidget, dock_widget_name) is None: + action = pyside_utils.find_child_by_pattern(sc, {"text": pane_name, "type": QtWidgets.QAction}) + action.trigger() + tool = sc.findChild(QtWidgets.QDockWidget, dock_widget_name) + return tool + + # 1) Open Script Canvas window + general.idle_enable(True) + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) + + # 2) Get the SC window object + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + + # 3) Open Variable Manager if not opened already + variable_manager = open_tool(sc, "VariableManager", "Variable Manager") + + # 4) Open Node Inspector if not opened already + node_inspector = open_tool(sc, "NodeInspector", "Node Inspector") + + # 5) Create new graph and a new variable in Variable manager + action = pyside_utils.find_child_by_pattern(sc, {"objectName": "action_New_Script", "type": QtWidgets.QAction}) + action.trigger() + graph_vars = variable_manager.findChild(QtWidgets.QTableView, "graphVariables") + add_button = variable_manager.findChild(QtWidgets.QPushButton, "addButton") + add_button.click() + # Select variable type + table_view = variable_manager.findChild(QtWidgets.QTableView, "variablePalette") + model_index = pyside_utils.find_child_by_pattern(table_view, "Boolean") + # Click on it to create variable + pyside_utils.item_view_index_mouse_click(table_view, model_index) + result = graph_vars.model().rowCount(QtCore.QModelIndex()) == 1 + var_mi = pyside_utils.find_child_by_pattern(graph_vars, "Variable 1") + result = result and (var_mi is not None) + Report.critical_result(Tests.variable_created, result) + + # 6) Click on the variable + pyside_utils.item_view_index_mouse_click(graph_vars, var_mi) + + # 7) Update name in Node Inspector and click on ENTER + helper.wait_for_condition( + lambda: node_inspector.findChild(QtWidgets.QWidget, "ContainerForRows") is not None, GENERAL_WAIT + ) + row_container = node_inspector.findChild(QtWidgets.QWidget, "ContainerForRows") + name_frame = row_container.findChild(QtWidgets.QWidget, "Name") + name_line_edit = name_frame.findChild(QtWidgets.QLineEdit) + name_line_edit.setText(TEST_NAME) + QtTest.QTest.keyClick(name_line_edit, Qt.Key_Return, Qt.NoModifier) + + # 8) Verify if the name is updated in Node inspector and Variable manager + helper.wait_for_condition(lambda: var_mi.data(Qt.DisplayRole) == TEST_NAME, GENERAL_WAIT) + Report.critical_result(Tests.node_inspector_rename, name_line_edit.text() == TEST_NAME) + Report.critical_result(Tests.variable_manager_rename, var_mi.data(Qt.DisplayRole) == TEST_NAME) + + # 9) Close Script Canvas window + general.close_pane("Script Canvas") + + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + + from utils import Report + + Report.start_test(NodeInspector_RenameVariable) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/NodePalette_ClearSelection.py b/AutomatedTesting/Gem/PythonTests/scripting/NodePalette_ClearSelection.py new file mode 100644 index 0000000000..de30e46767 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/NodePalette_ClearSelection.py @@ -0,0 +1,94 @@ +""" +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 ID: T92562993 +Test Case Title: Clicking the X button on the Search Box clears the currently entered string +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92562993 +""" + + +# fmt: off +class Tests(): + set_search_string = ("Search string is set", "Search string is not set") + search_string_cleared = ("Search string cleared as expected", "Search string not cleared") +# fmt: on + + +def NodePalette_ClearSelection(): + """ + Summary: + We enter some string in the Node Palette Search box, and click on the X button to verify if the + search string got cleared. + + Expected Behavior: + Clicking the X button on the Search Box clears the currently entered string + + Test Steps: + 1) Open Script Canvas window (Tools > Script Canvas) + 2) Get the SC window object + 3) Open Node Manager if not opened already + 4) Set some string in the Search box + 5) Verify if the test string is set + 6) Clear search string and verify if it is cleared + + 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 + """ + + from PySide2 import QtWidgets + + from utils import TestHelper as helper + + import azlmbr.legacy.general as general + + import pyside_utils + + TEST_STRING = "Test String" + + # 1) Open Script Canvas window (Tools > Script Canvas) + general.idle_enable(True) + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 3.0) + + # 2) Get the SC window object + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + + # 3) Open Node Manager if not opened already + if sc.findChild(QtWidgets.QDockWidget, "NodePalette") is None: + action = pyside_utils.find_child_by_pattern(sc, {"text": "Node Palette", "type": QtWidgets.QAction}) + action.trigger() + node_palette = sc.findChild(QtWidgets.QDockWidget, "NodePalette") + search_frame = node_palette.findChild(QtWidgets.QFrame, "searchFrame") + + # 4) Set some string in the Search box + search_box = search_frame.findChild(QtWidgets.QLineEdit, "searchFilter") + search_box.setText(TEST_STRING) + + # 5) Verify if the test string is set + Report.result(Tests.set_search_string, search_box.text() == TEST_STRING) + + # 6) Clear search string and verify if it is cleared + clear_text_button = search_frame.findChild(QtWidgets.QToolButton, "ClearToolButton") + clear_text_button.click() + Report.result(Tests.search_string_cleared, search_box.text() == "") + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + from utils import Report + + Report.start_test(NodePalette_ClearSelection) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/NodePalette_SelectNode.py b/AutomatedTesting/Gem/PythonTests/scripting/NodePalette_SelectNode.py new file mode 100644 index 0000000000..2ab76071a6 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/NodePalette_SelectNode.py @@ -0,0 +1,104 @@ +""" +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 ID: T92568940 +Test Case Title: Categories and Nodes can be selected +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92568940 +""" + + +# fmt: off +class Tests(): + category_selected = ("Category can be selected", "Category cannot be selected") + node_selected = ("Node can be selected", "Node cannot be selected") +# fmt: on + + +GENERAL_WAIT = 0.5 # seconds + + +def NodePalette_SelectNode(): + """ + Summary: + Categories and Nodes can be selected + + Expected Behavior: + When clicked on Node Palette, nodes and categories can be selected. + + Test Steps: + 1) Open Script Canvas window (Tools > Script Canvas) + 2) Get the SC window object + 3) Expand QTreeView + 4) Click on category and check if it is selected + 5) Click on node and check if it is selected + 6) Close Script Canvas window + + + 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 + """ + + CATEGORY = "AI" + NODE = "Find Path To Entity" + + from PySide2 import QtWidgets + import azlmbr.legacy.general as general + + import pyside_utils + from utils import TestHelper as helper + + # 1) Open Script Canvas window (Tools > Script Canvas) + general.idle_enable(True) + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) + + # 2) Get the SC window object + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + if sc.findChild(QtWidgets.QDockWidget, "NodePalette") is None: + action = pyside_utils.find_child_by_pattern(sc, {"text": "Node Palette", "type": QtWidgets.QAction}) + action.trigger() + node_palette = sc.findChild(QtWidgets.QDockWidget, "NodePalette") + tree = node_palette.findChild(QtWidgets.QTreeView, "treeView") + + # 3) Expand QTreeView + tree.expandAll() + + # 4) Click on category and check if it is selected + category_index = pyside_utils.find_child_by_hierarchy(tree, CATEGORY) + tree.scrollTo(category_index) + pyside_utils.item_view_index_mouse_click(tree, category_index) + pyside_utils.wait_for_condition(tree.selectedIndexes() and tree.selectedIndexes()[0] == category_index) + Report.result(Tests.category_selected, tree.selectedIndexes()[0] == category_index) + + # 5) Click on node and check if it is selected + node_index = pyside_utils.find_child_by_pattern(tree, NODE) + helper.wait_for_condition(lambda: tree.isExpanded(node_index), GENERAL_WAIT) + pyside_utils.item_view_index_mouse_click(tree, node_index) + pyside_utils.wait_for_condition(tree.selectedIndexes()[0] == node_index) + Report.result(Tests.node_selected, tree.selectedIndexes()[0] == node_index) + + # 6) Close Script Canvas window + general.close_pane("Script Canvas") + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + + from utils import Report + + Report.start_test(NodePalette_SelectNode) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/OnEntityActivatedDeactivated_PrintMessage.py b/AutomatedTesting/Gem/PythonTests/scripting/OnEntityActivatedDeactivated_PrintMessage.py new file mode 100644 index 0000000000..51b63c4268 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/OnEntityActivatedDeactivated_PrintMessage.py @@ -0,0 +1,184 @@ +""" +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 ID: T92569253 // T92569254 +Test Case Title: On Entity Activated // On Entity Deactivated +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92569253 // https://testrail.agscollab.com/index.php?/tests/view/92569254 +""" + + +# fmt: off +class Tests(): + level_created = ("Successfully created temp level", "Failed to create temp level") + controller_exists = ("Successfully found controller entity", "Failed to find controller entity") + activated_exists = ("Successfully found activated entity", "Failed to find activated entity") + deactivated_exists = ("Successfully found deactivated entity","Failed to find deactivated entity") + start_states_correct = ("Start states set up successfully", "Start states set up incorrectly") + game_mode_entered = ("Successfully entered game mode" "Failed to enter game mode") + lines_found = ("Successfully found expected prints", "Failed to find expected prints") + game_mode_exited = ("Successfully exited game mode" "Failed to exit game mode") +# fmt: on + + +def OnEntityActivatedDeactivated_PrintMessage(): + """ + Summary: + Verify that the On Entity Activation node is working as expected + + Expected Behavior: + Upon entering game mode, the Controller entity will wait 1 second and then activate the ActivationTest + entity. The script attached to ActivationTest will print out a message on activation. The Controller + will also deactivate the DeactivationTest entity, which should print a message. + + Test Steps: + 1) Create temp level + 2) Setup the level + 3) Validate the entities + 4) Start the Tracer + 5) Enter Game Mode + 6) Validate Print message + 7) 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 utils import TestHelper as helper + from editor_entity_utils import EditorEntity as Entity + from utils import Report + from utils import Tracer + + import azlmbr.legacy.general as general + + EditorEntity = str + LEVEL_NAME = "tmp_level" + WAIT_TIME = 3.0 # SECONDS + EXPECTED_LINES = ["Activator Script: Activated", "Deactivator Script: Deactivated"] + controller_dict = { + "name": "Controller", + "status": "active", + "path": os.path.join("ScriptCanvas", "OnEntityActivatedScripts", "controller.scriptcanvas") + } + activated_dict = { + "name": "ActivationTest", + "status": "inactive", + "path": os.path.join("ScriptCanvas", "OnEntityActivatedScripts", "activator.scriptcanvas") + } + deactivated_dict = { + "name": "DeactivationTest", + "status": "active", + "path": os.path.join("ScriptCanvas", "OnEntityActivatedScripts", "deactivator.scriptcanvas") + } + + def get_asset(asset_path): + return azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, "GetAssetIdByPath", asset_path, azlmbr.math.Uuid(), False) + + def setup_level(): + def create_editor_entity(entity_dict:dict, entity_to_activate:EditorEntity=None, entity_to_deactivate:EditorEntity=None) -> EditorEntity: + entity = Entity.create_editor_entity(entity_dict["name"]) + entity.set_start_status(entity_dict["status"]) + sc_component = entity.add_component("Script Canvas") + sc_component.set_component_property_value("Script Canvas Asset|Script Canvas Asset", get_asset(entity_dict["path"])) + + if entity_dict["name"] == "Controller": + sc_component.get_property_tree() + sc_component.set_component_property_value("Properties|Variable Fields|Variables|[0]|Name,Value|Datum|Datum|EntityToActivate", entity_to_activate.id) + sc_component.set_component_property_value("Properties|Variable Fields|Variables|[1]|Name,Value|Datum|Datum|EntityToDeactivate", entity_to_deactivate.id) + return entity + + activated = create_editor_entity(activated_dict) + deactivated = create_editor_entity(deactivated_dict) + create_editor_entity(controller_dict, activated, deactivated) + + def validate_entity_exist(entity_name: str, test_tuple: tuple): + """ + Validate the entity with the given name exists in the level + :return: entity: editor entity object + """ + entity = Entity.find_editor_entity(entity_name) + Report.critical_result(test_tuple, entity.id.IsValid()) + return entity + + def validate_start_state(entity:EditorEntity, expected_state:str): + """ + Validate that the starting state of the entity is correct, if it isn't then attempt to rectify and recheck. + :return: bool: Whether state is set as expected + """ + state_options = { + "active": azlmbr.globals.property.EditorEntityStartStatus_StartActive, + "inactive": azlmbr.globals.property.EditorEntityStartStatus_StartInactive, + "editor": azlmbr.globals.property.EditorEntityStartStatus_EditorOnly, + } + if expected_state.lower() not in state_options.keys(): + raise ValueError(f"{expected_state} is an invalid option; valid options: active, inactive, or editor.") + + state = entity.get_start_status() + if state != state_options[expected_state]: + # If state fails to set, set_start_status will assert + entity.set_start_status(expected_state) + return True + + def validate_entities_in_level(): + controller = validate_entity_exist(controller_dict["name"], Tests.controller_exists) + state1_correct = validate_start_state(controller, controller_dict["status"]) + + act_tester = validate_entity_exist(activated_dict["name"], Tests.activated_exists) + state2_correct = validate_start_state(act_tester, activated_dict["status"]) + + deac_tester = validate_entity_exist(deactivated_dict["name"], Tests.deactivated_exists) + state3_correct = validate_start_state(deac_tester, deactivated_dict["status"]) + + all_states_correct = state1_correct and state2_correct and state3_correct + Report.critical_result(Tests.start_states_correct, all_states_correct) + + 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) Setup the level + setup_level() + + # 3) Validate the entities + validate_entities_in_level() + + # 4) Start the Tracer + with Tracer() as section_tracer: + + # 5) Enter Game Mode + helper.enter_game_mode(Tests.game_mode_entered) + + # 6) Validate Print message + helper.wait_for_condition(lambda: locate_expected_lines(EXPECTED_LINES), WAIT_TIME) + + Report.result(Tests.lines_found, locate_expected_lines(EXPECTED_LINES)) + + # 7) Exit game mode + helper.exit_game_mode(Tests.game_mode_exited) + + +if __name__ == "__main__": + import ImportPathHelper as imports + imports.init() + + from utils import Report + + Report.start_test(OnEntityActivatedDeactivated_PrintMessage) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/Opening_Closing_Pane.py b/AutomatedTesting/Gem/PythonTests/scripting/Opening_Closing_Pane.py index 6100285092..666f052240 100755 --- a/AutomatedTesting/Gem/PythonTests/scripting/Opening_Closing_Pane.py +++ b/AutomatedTesting/Gem/PythonTests/scripting/Opening_Closing_Pane.py @@ -17,7 +17,6 @@ URLs of the test case: https://testrail.agscollab.com/index.php?/cases/view/1702 # fmt: off class Tests(): - open_sc_window = ("Script Canvas window is opened", "Failed to open Script Canvas window") default_visible = ("All the panes visible by default", "One or more panes do not visible by default") open_panes = ("All the Panes opened successfully", "Failed to open one or more panes") close_pane = ("All the Panes closed successfully", "Failed to close one or more panes") @@ -49,11 +48,6 @@ def Opening_Closing_Pane(): :return: None """ - # Helper imports - import ImportPathHelper as imports - - imports.init() - from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper import editor_python_test_tools.pyside_utils as pyside_utils @@ -82,8 +76,7 @@ def Opening_Closing_Pane(): # 1) Open Script Canvas window (Tools > Script Canvas) general.open_pane("Script Canvas") - is_sc_visible = helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) - Report.result(Tests.open_sc_window, is_sc_visible) + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) # 2) Restore default layout editor_window = pyside_utils.get_editor_main_window() diff --git a/AutomatedTesting/Gem/PythonTests/scripting/Pane_RetainOnSCRestart.py b/AutomatedTesting/Gem/PythonTests/scripting/Pane_RetainOnSCRestart.py new file mode 100644 index 0000000000..fa5e9e6068 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/Pane_RetainOnSCRestart.py @@ -0,0 +1,164 @@ +""" +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 ID: C1702821 // C1702832 +Test Case Title: Retain visibility, size and location upon Script Canvas restart +URLs of the test case: https://testrail.agscollab.com/index.php?/cases/view/1702821 and + https://testrail.agscollab.com/index.php?/cases/view/1702832 +""" + + +# fmt: off +class Tests(): + relaunch_sc = ("Script Canvas window is relaunched", "Failed to relaunch Script Canvas window") + test_panes_visible = ("All the test panes are opened", "Failed to open one or more test panes") + close_pane_1 = ("Test pane 1 is closed", "Failed to close test pane 1") + visiblity_retained = ("Test pane retained its visiblity on SC restart", "Failed to retain visiblity of test pane on SC restart") + resize_pane_3 = ("Test pane 3 resized successfully", "Failed to resize Test pane 3") + size_retained = ("Test pane retained its size on SC restart", "Failed to retain size of test pane on SC restart") + location_changed = ("Location of test pane 2 changed successfully", "Failed to change locatio of test pane 2") + location_retained = ("Test pane retained its location on SC restart", "Failed to retain location of test pane on SC restart") +# fmt: on + + +def Pane_RetainOnSCRestart(): + """ + Summary: + The Script Canvas window is opened to verify if Script canvas panes can retain its visibility, size and location + upon ScriptCanvas restart. + + Expected Behavior: + The ScriptCanvas pane retain it's visiblity, size and location upon ScriptCanvas restart. + + Test Steps: + 1) Open Script Canvas window (Tools > Script Canvas) + 2) Make sure test panes are open and visible + 3) Close test pane 1 + 4) Change dock location of test pane 2 + 5) Resize test pane 3 + 6) Relaunch Script Canvas + 7) Verify if test pane 1 retain its visiblity + 8) Verify if location of test pane 2 is retained + 9) Verify if size of test pane 3 is retained + 10) Restore default layout and close SC window + + 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 + """ + + # Helper imports + from utils import Report + from utils import TestHelper as helper + import pyside_utils + + # Open 3D Engine Imports + import azlmbr.legacy.general as general + + # Pyside imports + from PySide2 import QtCore, QtWidgets + from PySide2.QtCore import Qt + + # Constants + TEST_PANE_1 = "NodePalette" # test visibility + TEST_PANE_2 = "VariableManager" # test location + TEST_PANE_3 = "NodeInspector" # test size + SCALE_INT = 10 # Random resize scale integer + DOCKAREA = Qt.TopDockWidgetArea # Preferred top area since no widget is docked on top + + def click_menu_option(window, option_text): + action = pyside_utils.find_child_by_pattern(window, {"text": option_text, "type": QtWidgets.QAction}) + action.trigger() + + def find_pane(window, pane_name): + return window.findChild(QtWidgets.QDockWidget, pane_name) + + # Test starts here + general.idle_enable(True) + + # 1) Open Script Canvas window (Tools > Script Canvas) + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 3.0) + + # 2) Make sure test panes are open and visible + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + click_menu_option(sc, "Restore Default Layout") + test_pane_1 = sc.findChild(QtWidgets.QDockWidget, TEST_PANE_1) + test_pane_2 = sc.findChild(QtWidgets.QDockWidget, TEST_PANE_2) + test_pane_3 = sc.findChild(QtWidgets.QDockWidget, TEST_PANE_3) + + Report.result( + Tests.test_panes_visible, test_pane_1.isVisible() and test_pane_2.isVisible() and test_pane_3.isVisible() + ) + + # Initiate try block here to restore default in finally block + try: + # 3) Close test pane + test_pane_1.close() + Report.result(Tests.close_pane_1, not test_pane_1.isVisible()) + + # 4) Change dock location of test pane 2 + sc_main = sc.findChild(QtWidgets.QMainWindow) + sc_main.addDockWidget(DOCKAREA, find_pane(sc_main, TEST_PANE_2), QtCore.Qt.Vertical) + Report.result(Tests.location_changed, sc_main.dockWidgetArea(find_pane(sc_main, TEST_PANE_2)) == DOCKAREA) + + # 5) Resize test pane 3 + initial_size = test_pane_3.frameSize() + test_pane_3.resize(initial_size.width() + SCALE_INT, initial_size.height() + SCALE_INT) + new_size = test_pane_3.frameSize() + resize_success = ( + abs(initial_size.width() - new_size.width()) == abs(initial_size.height() - new_size.height()) == SCALE_INT + ) + Report.result(Tests.resize_pane_3, resize_success) + + # 6) Relaunch Script Canvas + general.close_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 2.0) + + general.open_pane("Script Canvas") + sc_visible = helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) + Report.result(Tests.relaunch_sc, sc_visible) + + # 7) Verify if test pane 1 retain its visiblity + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + Report.result(Tests.visiblity_retained, not find_pane(sc, TEST_PANE_1).isVisible()) + + # 8) Verify if location of test pane 2 is retained + sc_main = sc.findChild(QtWidgets.QMainWindow) + Report.result(Tests.location_retained, sc_main.dockWidgetArea(find_pane(sc_main, TEST_PANE_2)) == DOCKAREA) + + # 9) Verify if size of test pane 3 is retained + test_pane_3 = sc.findChild(QtWidgets.QDockWidget, TEST_PANE_3) + retained_size = test_pane_3.frameSize() + retain_success = retained_size != initial_size + Report.result(Tests.size_retained, retain_success) + + finally: + # 10) Restore default layout and close SC window + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + click_menu_option(sc, "Restore Default Layout") + sc.close() + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + + from utils import Report + + Report.start_test(Pane_RetainOnSCRestart) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/Resizing_Pane.py b/AutomatedTesting/Gem/PythonTests/scripting/Resizing_Pane.py index 9262e76cb0..180f577953 100755 --- a/AutomatedTesting/Gem/PythonTests/scripting/Resizing_Pane.py +++ b/AutomatedTesting/Gem/PythonTests/scripting/Resizing_Pane.py @@ -16,7 +16,6 @@ URLs of the test case: https://testrail.agscollab.com/index.php?/cases/view/1702 # fmt: off class Tests(): - open_sc_window = ("Script Canvas window is opened", "Failed to open Script Canvas window") open_pane = ("Pane opened successfully", "Failed to open pane") resize_pane = ("Pane window resized successfully", "Failed to resize pane window") # fmt: on @@ -46,11 +45,6 @@ def Resizing_Pane(): :return: None """ - # Helper imports - import ImportPathHelper as imports - - imports.init() - from editor_python_test_tools.utils import Report from editor_python_test_tools.utils import TestHelper as helper import editor_python_test_tools.pyside_utils as pyside_utils @@ -76,8 +70,7 @@ def Resizing_Pane(): # 1) Open Script Canvas window (Tools > Script Canvas) general.open_pane("Script Canvas") - is_sc_visible = helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) - Report.result(Tests.open_sc_window, is_sc_visible) + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) # 2) Restore default layout editor_window = pyside_utils.get_editor_main_window() diff --git a/AutomatedTesting/Gem/PythonTests/scripting/ScriptCanvas_ChangingAssets.py b/AutomatedTesting/Gem/PythonTests/scripting/ScriptCanvas_ChangingAssets.py new file mode 100644 index 0000000000..38d60ad871 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/ScriptCanvas_ChangingAssets.py @@ -0,0 +1,116 @@ +""" +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 ID: T92562986 +Test Case Title: Changing the assigned Script Canvas Asset on an entity properly updates +level functionality +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92562986 +""" + + +# fmt: off +class Tests(): + level_created = ("New level created", "New level not created") + entity_created = ("Test Entity created", "Test Entity not created") + game_mode_entered = ("Game Mode successfully entered", "Game mode failed to enter") + game_mode_exited = ("Game Mode successfully exited", "Game mode failed to exited") + found_lines = ("Expected log lines were found", "Expected log lines were not found") +# fmt: on + + +def ScriptCanvas_ChangingAssets(): + """ + Summary: + Changing the assigned Script Canvas Asset on an entity properly updates level functionality + + Expected Behavior: + When game mode is entered, respective strings of assigned assets should be printed + + Test Steps: + 1) Create temp level + 2) Create new entity + 3) Start Tracer + 4) Set first script and evaluate + 5) Set second script and evaluate + + + 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 utils import TestHelper as helper + from utils import Tracer + import hydra_editor_utils as hydra + import azlmbr.legacy.general as general + import azlmbr.math as math + import azlmbr.asset as asset + import azlmbr.bus as bus + import azlmbr.paths as paths + + LEVEL_NAME = "tmp_level" + ASSET_1 = os.path.join("scriptcanvas", "ScriptCanvas_TwoComponents0.scriptcanvas") + ASSET_2 = os.path.join("scriptcanvas", "ScriptCanvas_TwoComponents1.scriptcanvas") + EXP_LINE_1 = "Greetings from the first script" + EXP_LINE_2 = "Greetings from the second script" + WAIT_TIME = 3.0 # SECONDS + + def get_asset(asset_path): + return asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False) + + def find_expected_line(expected_line): + found_lines = [printInfo.message.strip() for printInfo in section_tracer.prints] + return expected_line in found_lines + + def set_asset_evaluate(test_entity, ASSET_PATH, EXP_LINE): + # Set Script Canvas entity + test_entity.get_set_test(0, "Script Canvas Asset|Script Canvas Asset", get_asset(ASSET_PATH)) + + # Enter/exit game mode + helper.enter_game_mode(Tests.game_mode_entered) + helper.wait_for_condition(lambda: find_expected_line(EXP_LINE), WAIT_TIME) + Report.result(Tests.found_lines, find_expected_line(EXP_LINE)) + helper.exit_game_mode(Tests.game_mode_exited) + + + # 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 new entity + position = math.Vector3(512.0, 512.0, 32.0) + test_entity = hydra.Entity("test_entity") + test_entity.create_entity(position, ["Script Canvas"]) + + # 3) Start Tracer + with Tracer() as section_tracer: + + # 4) Set first script and evaluate + set_asset_evaluate(test_entity, ASSET_1, EXP_LINE_1) + + # 5) Set second script and evaluate + set_asset_evaluate(test_entity, ASSET_2, EXP_LINE_2) + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + from utils import Report + + Report.start_test(ScriptCanvas_ChangingAssets) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/ScriptCanvas_TwoComponents.py b/AutomatedTesting/Gem/PythonTests/scripting/ScriptCanvas_TwoComponents.py new file mode 100644 index 0000000000..896bee96e5 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/ScriptCanvas_TwoComponents.py @@ -0,0 +1,118 @@ +""" +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 ID: T92563190 +Test Case Title: A single Entity with two Script Canvas components works properly +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92563190 +""" + + +# fmt: off +class Tests(): + level_created = ("New level created", "New level not created") + game_mode_entered = ("Game Mode successfully entered", "Game mode failed to enter") + game_mode_exited = ("Game Mode successfully exited", "Game mode failed to exited") + found_lines = ("Expected log lines were found", "Expected log lines were not found") +# fmt: on + + +class LogLines: + expected_lines = ["Greetings from the first script", "Greetings from the second script"] + + +def ScriptCanvas_TwoComponents(): + """ + Summary: + A test entity contains two Script Canvas components with different unique script canvas files. + Each of these files will have a print node set to activate on graph start. + + Expected Behavior: + When game mode is entered, two unique strings should be printed out to the console + + Test Steps: + 1) Create level + 2) Create entity with SC components + 3) Start Tracer + 4) Enter game mode + 5) Wait for expected lines to be found + 6) Report if expected lines were found + 7) 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 utils import TestHelper as helper + import hydra_editor_utils as hydra + from utils import Report + from utils import Tracer + import azlmbr.legacy.general as general + import azlmbr.math as math + import azlmbr.asset as asset + import azlmbr.bus as bus + + LEVEL_NAME = "tmp_level" + ASSET_1 = os.path.join("scriptcanvas", "ScriptCanvas_TwoComponents0.scriptcanvas") + ASSET_2 = os.path.join("scriptcanvas", "ScriptCanvas_TwoComponents1.scriptcanvas") + WAIT_TIME = 3.0 # SECONDS + + def get_asset(asset_path): + return asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False) + + def locate_expected_lines(): + found_lines = [] + for printInfo in section_tracer.prints: + found_lines.append(printInfo.message.strip()) + + return all(line in found_lines for line in LogLines.expected_lines) + + # 1) Create 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 entity with SC components + position = math.Vector3(512.0, 512.0, 32.0) + test_entity = hydra.Entity("test_entity") + test_entity.create_entity(position, ["Script Canvas", "Script Canvas"]) + test_entity.get_set_test(0, "Script Canvas Asset|Script Canvas Asset", get_asset(ASSET_1)) + test_entity.get_set_test(1, "Script Canvas Asset|Script Canvas Asset", get_asset(ASSET_2)) + + # 3) Start Tracer + with Tracer() as section_tracer: + + # 4) Enter game mode + helper.enter_game_mode(Tests.game_mode_entered) + + # 5) Wait for expected lines to be found + helper.wait_for_condition(locate_expected_lines, WAIT_TIME) + + # 6) Report if expected lines were found + Report.result(Tests.found_lines, locate_expected_lines()) + + # 7) Exit game mode + helper.exit_game_mode(Tests.game_mode_exited) + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + + from utils import Report + + Report.start_test(ScriptCanvas_TwoComponents) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/ScriptCanvas_TwoEntities.py b/AutomatedTesting/Gem/PythonTests/scripting/ScriptCanvas_TwoEntities.py new file mode 100644 index 0000000000..401e6c0271 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/ScriptCanvas_TwoEntities.py @@ -0,0 +1,107 @@ +""" +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 ID: T92563191 +Test Case Title: Two Entities can use the same Graph asset successfully at RunTime +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92563191 +""" + + +# fmt: off +class Tests(): + level_created = ("New level created", "New level not created") + game_mode_entered = ("Game Mode successfully entered", "Game mode failed to enter") + game_mode_exited = ("Game Mode successfully exited", "Game mode failed to exited") + found_lines = ("Expected log lines were found", "Expected log lines were not found") +# fmt: on + + +def ScriptCanvas_TwoEntities(): + """ + Summary: + Two Entities can use the same Graph asset successfully at RunTime. The script canvas asset + attached to the enties will print the respective entity names. + + Expected Behavior: + When game mode is entered, respective strings of different entities should be printed. + + Test Steps: + 1) Create temp level + 2) Create two new entities with different names + 3) Set ScriptCanvas asset to both the entities + 4) Enter/Exit game mode and verify log lines + + + 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 utils import TestHelper as helper + from utils import Tracer + import hydra_editor_utils as hydra + import azlmbr.legacy.general as general + import azlmbr.math as math + import azlmbr.asset as asset + import azlmbr.bus as bus + + LEVEL_NAME = "tmp_level" + ASSET_PATH = os.path.join("scriptcanvas", "T92563191_test.scriptcanvas") + EXPECTED_LINES = ["Entity Name: test_entity_1", "Entity Name: test_entity_2"] + WAIT_TIME = 0.5 # SECONDS + + def get_asset(asset_path): + return asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False) + + # 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 two new entities with different names + position = math.Vector3(512.0, 512.0, 32.0) + test_entity_1 = hydra.Entity("test_entity_1") + test_entity_1.create_entity(position, ["Script Canvas"]) + + test_entity_2 = hydra.Entity("test_entity_2") + test_entity_2.create_entity(position, ["Script Canvas"]) + + # 3) Set ScriptCanvas asset to both the entities + test_entity_1.get_set_test(0, "Script Canvas Asset|Script Canvas Asset", get_asset(ASSET_PATH)) + test_entity_2.get_set_test(0, "Script Canvas Asset|Script Canvas Asset", get_asset(ASSET_PATH)) + + # 4) Enter/Exit game mode and verify log lines + with Tracer() as section_tracer: + + helper.enter_game_mode(Tests.game_mode_entered) + # wait for WAIT_TIME to let the script print strings + general.idle_wait(WAIT_TIME) + helper.exit_game_mode(Tests.game_mode_exited) + + found_lines = [printInfo.message.strip() for printInfo in section_tracer.prints] + result = all(line in found_lines for line in EXPECTED_LINES) + + Report.result(Tests.found_lines, result) + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + from utils import Report + + Report.start_test(ScriptCanvas_TwoEntities) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvents_SendReceiveAcrossMultiple.py b/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvents_SendReceiveAcrossMultiple.py new file mode 100644 index 0000000000..d671229cdf --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvents_SendReceiveAcrossMultiple.py @@ -0,0 +1,120 @@ +""" +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 ID: T92567321 +Test Case Title: Script Events: Can send and receive a script event across multiple entities successfully +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92567321 +""" + + +# fmt: off +class Tests(): + level_created = ("Successfully created temporary level", "Failed to create temporary level") + entitya_created = ("Successfully created EntityA", "Failed to create EntityA") + entityb_created = ("Successfully created EntityB", "Failed to create EntityB") + 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_SendReceiveAcrossMultiple(): + """ + Summary: + EntityA and EntityB will be created in a level. Attached to both will be a Script Canvas component. The Script Event created for the test will be sent from EntityA to EntityB. + + Expected Behavior: + The output of the Script Event should be printed to the console + + Test Steps: + 1) Create test level + 2) Create EntityA/EntityB (add scriptcanvas files part of entity setup) + 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 + + LEVEL_NAME = "tmp_level" + WAIT_TIME = 3.0 + ASSET_PREFIX = "T92567321" + asset_paths = { + "event": os.path.join("TestAssets", f"{ASSET_PREFIX}.scriptevents"), + "assetA": os.path.join("ScriptCanvas", f"{ASSET_PREFIX}A.scriptcanvas"), + "assetB": os.path.join("ScriptCanvas", f"{ASSET_PREFIX}B.scriptcanvas"), + } + sc_for_entities = { + "EntityA": asset_paths["assetA"], + "EntityB": asset_paths["assetB"] + } + EXPECTED_LINES = ["Incoming Message Received"] + + def get_asset(asset_path): + return azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, "GetAssetIdByPath", asset_path, azlmbr.math.Uuid(), False) + + def create_editor_entity(name, sc_asset): + entity = Entity.create_editor_entity(name) + sc_comp = entity.add_component("Script Canvas") + sc_comp.set_component_property_value("Script Canvas Asset|Script Canvas Asset", get_asset(sc_asset)) + Report.critical_result(Tests.__dict__[name.lower()+"_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 EntityA/EntityB + for key in sc_for_entities.keys(): + create_editor_entity(key, sc_for_entities[key]) + + # 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_SendReceiveAcrossMultiple) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvents_SendReceiveSuccessfully.py b/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvents_SendReceiveSuccessfully.py new file mode 100644 index 0000000000..b5e26d14ae --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/ScriptEvents_SendReceiveSuccessfully.py @@ -0,0 +1,110 @@ +""" +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 ID: T92567320 +Test Case Title: Script Events: Can send and receive a script event successfully +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92567320 +""" + + +# 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_SendReceiveSuccessfully(): + """ + Summary: + An entity exists in the level that contains a Script Canvas component. In the graph is both a Send Event + and a Receive Event. + + 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 = ["T92567320: Message Received"] + SC_ASSET_PATH = os.path.join("ScriptCanvas", "T92567320.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_SendReceiveSuccessfully) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Active.py b/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Active.py index 8c34f29ebf..d87f2986bf 100755 --- a/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Active.py +++ b/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Active.py @@ -12,22 +12,243 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. import pytest import os import sys +sys.path.append(os.path.dirname(__file__)) +import ImportPathHelper as imports +imports.init() + +import hydra_test_utils as hydra +import ly_test_tools.environment.file_system as file_system from ly_test_tools import LAUNCHERS - -sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared') - from base import TestAutomationBase +TEST_DIRECTORY = os.path.dirname(__file__) + + @pytest.mark.SUITE_periodic @pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("project", ["AutomatedTesting"]) class TestAutomation(TestAutomationBase): - + @pytest.mark.test_case_id("C1702834", "C1702823") + def test_Opening_Closing_Pane(self, request, workspace, editor, launcher_platform): + from . import Opening_Closing_Pane as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("C1702824") def test_Docking_Pane(self, request, workspace, editor, launcher_platform): from . import Docking_Pane as test_module self._run_test(request, workspace, editor, test_module) + @pytest.mark.test_case_id("C1702829") def test_Resizing_Pane(self, request, workspace, editor, launcher_platform): from . import Resizing_Pane as test_module self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92563190") + @pytest.mark.parametrize("level", ["tmp_level"]) + def test_ScriptCanvas_TwoComponents(self, request, workspace, editor, launcher_platform, 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 ScriptCanvas_TwoComponents as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92562986") + @pytest.mark.parametrize("level", ["tmp_level"]) + def test_ScriptCanvas_ChangingAssets(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 ScriptCanvas_ChangingAssets as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92569079", "T92569081") + def test_Graph_ZoomInZoomOut(self, request, workspace, editor, launcher_platform): + from . import Graph_ZoomInZoomOut as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92568940") + def test_NodePalette_SelectNode(self, request, workspace, editor, launcher_platform): + from . import NodePalette_SelectNode as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92569253") + @pytest.mark.test_case_id("T92569254") + @pytest.mark.parametrize("level", ["tmp_level"]) + def test_OnEntityActivatedDeactivated_PrintMessage(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 OnEntityActivatedDeactivated_PrintMessage as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92562993") + def test_NodePalette_ClearSelection(self, request, workspace, editor, launcher_platform, project): + from . import NodePalette_ClearSelection as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92563191") + @pytest.mark.parametrize("level", ["tmp_level"]) + def test_ScriptCanvas_TwoEntities(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 ScriptCanvas_TwoEntities as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92569013") + def test_AssetEditor_CreateScriptEventFile(self, request, workspace, editor, launcher_platform, project): + def teardown(): + file_system.delete( + [os.path.join(workspace.paths.project(), "ScriptCanvas", "test_file.scriptevent")], True, True + ) + request.addfinalizer(teardown) + file_system.delete( + [os.path.join(workspace.paths.project(), "ScriptCanvas", "test_file.scriptevent")], True, True + ) + from . import AssetEditor_CreateScriptEventFile as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92569165", "T92569167", "T92569168", "T92569170") + def test_Toggle_ScriptCanvasTools(self, request, workspace, editor, launcher_platform): + from . import Toggle_ScriptCanvasTools as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92568982") + def test_NodeInspector_RenameVariable(self, request, workspace, editor, launcher_platform, project): + from . import NodeInspector_RenameVariable as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92569137") + def test_Debugging_TargetMultipleGraphs(self, request, workspace, editor, launcher_platform, project): + from . import Debugging_TargetMultipleGraphs as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92568856") + @pytest.mark.parametrize("level", ["tmp_level"]) + def test_Debugging_TargetMultipleEntities(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 Debugging_TargetMultipleEntities as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92569049", "T92569051") + def test_EditMenu_UndoRedo(self, request, workspace, editor, launcher_platform, project): + from . import EditMenu_UndoRedo as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("C1702825", "C1702831") + def test_UnDockedPane_CloseSCWindow(self, request, workspace, editor, launcher_platform): + from . import UnDockedPane_CloseSCWindow as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92562978") + @pytest.mark.parametrize("level", ["tmp_level"]) + def test_Entity_AddScriptCanvasComponent(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 Entity_AddScriptCanvasComponent as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("C1702821", "C1702832") + def test_Pane_RetainOnSCRestart(self, request, workspace, editor, launcher_platform): + from . import Pane_RetainOnSCRestart as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92567321") + @pytest.mark.parametrize("level", ["tmp_level"]) + def test_ScriptEvents_SendReceiveAcrossMultiple(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_SendReceiveAcrossMultiple as test_module + self._run_test(request, workspace, editor, test_module) + + @pytest.mark.test_case_id("T92567320") + @pytest.mark.parametrize("level", ["tmp_level"]) + def test_ScriptEvents_SendReceiveSuccessfully(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_SendReceiveSuccessfully as test_module + self._run_test(request, workspace, editor, test_module) + +# NOTE: We had to use hydra_test_utils.py, as TestAutomationBase run_test method +# fails because of pyside_utils import +@pytest.mark.SUITE_periodic +@pytest.mark.parametrize("launcher_platform", ["windows_editor"]) +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +class TestScriptCanvasTests(object): + """ + The following tests use hydra_test_utils.py to launch the editor and validate the results. + """ + + @pytest.mark.test_case_id("T92569037", "T92569039") + def test_FileMenu_New_Open(self, request, editor, launcher_platform): + expected_lines = [ + "File->New action working as expected: True", + "File->Open action working as expected: True", + ] + hydra.launch_and_validate_results( + request, TEST_DIRECTORY, editor, "FileMenu_New_Open.py", expected_lines, auto_test_mode=False, timeout=60, + ) + + @pytest.mark.test_case_id("T92568942") + def test_AssetEditor_NewScriptEvent(self, request, editor, launcher_platform): + expected_lines = [ + "New Script event action found: True", + "Asset Editor opened: True", + "Asset Editor created with new asset: True", + "New Script event created in Asset Editor: True", + ] + hydra.launch_and_validate_results( + request, + TEST_DIRECTORY, + editor, + "AssetEditor_NewScriptEvent.py", + expected_lines, + auto_test_mode=False, + timeout=60, + ) + + @pytest.mark.test_case_id("T92563068", "T92563070") + def test_GraphClose_SavePrompt(self, request, editor, launcher_platform): + expected_lines = [ + "New graph created: True", + "Save prompt opened as expected: True", + "Close button worked as expected: True", + ] + hydra.launch_and_validate_results( + request, + TEST_DIRECTORY, + editor, + "GraphClose_SavePrompt.py", + expected_lines, + auto_test_mode=False, + timeout=60, + ) + + @pytest.mark.test_case_id("T92564789", "T92568873") + def test_VariableManager_CreateDeleteVars(self, request, editor, launcher_platform): + var_types = ["Boolean", "Color", "EntityID", "Number", "String", "Transform", "Vector2", "Vector3", "Vector4"] + expected_lines = [f"Success: {var_type} variable is created" for var_type in var_types] + expected_lines.extend([f"Success: {var_type} variable is deleted" for var_type in var_types]) + hydra.launch_and_validate_results( + request, + TEST_DIRECTORY, + editor, + "VariableManager_CreateDeleteVars.py", + expected_lines, + auto_test_mode=False, + timeout=60, + ) \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonTests/scripting/Toggle_ScriptCanvasTools.py b/AutomatedTesting/Gem/PythonTests/scripting/Toggle_ScriptCanvasTools.py new file mode 100644 index 0000000000..4024e28277 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/Toggle_ScriptCanvasTools.py @@ -0,0 +1,137 @@ +""" +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 ID: C92569165, C92569167, C92569168, C92569170 +Test Case Title: Tools > Node Palette toggles the Node Palette + Tools > Node Inspector toggles the Node Inspector + Tools > Bookmarks toggles the Bookmarks + Tools > Variable Manager toggles the Variable Manager + +URLs of the test case: https://testrail.agscollab.com/index.php?/cases/view/92569165 + https://testrail.agscollab.com/index.php?/cases/view/92569167 + https://testrail.agscollab.com/index.php?/cases/view/92569168 + https://testrail.agscollab.com/index.php?/cases/view/92569170 +""" + + +# fmt: off +class Tests(): + node_palette_opened = ("NodePalette is opened successfully", "Failed to open NodePalette") + node_inspector_opened = ("NodeInspector is opened successfully", "Failed to open NodeInspector") + bookmark_opened = ("Bookmarks is opened successfully", "Failed to open Bookmarks") + variable_manager_opened = ("VariableManager is opened successfully", "Failed to open VariableManager") + node_palette_closed_by_start = ("NodePalette is closed successfully", "Failed to close NodePalette") + node_inspector_closed_by_start = ("NodeInspector is closed successfully", "Failed to close NodeInspector") + bookmark_closed_by_start = ("Bookmarks is closed successfully", "Failed to close Bookmarks") + variable_manager_closed_by_start = ("VariableManager is closed successfully", "Failed to close VariableManager") + node_palette_closed_by_end = ("NodePalette is closed successfully", "Failed to close NodePalette") + node_inspector_closed_by_end = ("NodeInspector is closed successfully", "Failed to close NodeInspector") + bookmark_closed_by_end = ("Bookmarks is closed successfully", "Failed to close Bookmarks") + variable_manager_closed_by_end = ("VariableManager is closed successfully", "Failed to close VariableManager") +# fmt: on + + +def Toggle_ScriptCanvasTools(): + """ + Summary: + Toggle Node Palette, Node Inspector, Bookmarks and Variable Manager in Script Canvas. + Make sure each pane opens and closes successfully. + + Expected Behavior: + Each pane opens and closes successfully. + + Test Steps: + 1) Open Script Canvas window (Tools > Script Canvas) + 2) Make sure Node Palette, Node Inspector, Bookmarks and Variable Manager panes are closed in Script Canvas window + 3) Open Node Palette, Node Inspector, Bookmarks and Variable Manager in Script Canvas window + 4) Close Node Palette, Node Inspector, Bookmarks and Variable Manager in Script Canvas window + 5) Restore default layout + 6) Close Script Canvas window + + 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 + """ + + from utils import Report + from utils import TestHelper as helper + import pyside_utils + + # Open 3D Engine imports + import azlmbr.legacy.general as general + + # Pyside imports + from PySide2 import QtWidgets + + def click_menu_option(window, option_text): + action = pyside_utils.find_child_by_pattern(window, {"text": option_text, "type": QtWidgets.QAction}) + action.trigger() + + def find_pane(window, pane_name): + return window.findChild(QtWidgets.QDockWidget, pane_name) + + def close_tool(window, pane_widget, test_tuple): + pane = find_pane(window, pane_widget) + pane.close() + Report.result(test_tuple, not pane.isVisible()) + + def open_tool(window, pane_widget, tool, test_tuple): + pane = find_pane(window, pane_widget) + if not pane.isVisible(): + click_menu_option(window, tool) + pane = find_pane(window, pane_widget) + Report.result(test_tuple, pane.isVisible()) + + # Test starts here + general.idle_enable(True) + + # 1) Open Script Canvas window (Tools > Script Canvas) + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) + + # 2) Make sure Node Palette, Node Inspector, Bookmarks and Variable Manager panes are closed in Script Canvas window + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + close_tool(sc, "NodePalette", Tests.node_palette_closed_by_start) + close_tool(sc, "NodeInspector", Tests.node_inspector_closed_by_start) + close_tool(sc, "BookmarkDockWidget", Tests.bookmark_closed_by_start) + close_tool(sc, "VariableManager", Tests.variable_manager_closed_by_start) + + # 3) Open Node Palette, Node Inspector, Bookmarks and Variable Manager in Script Canvas window + open_tool(sc, "NodePalette", "Node Palette", Tests.node_palette_opened) + open_tool(sc, "NodeInspector", "Node Inspector", Tests.node_inspector_opened) + open_tool(sc, "BookmarkDockWidget", "Bookmarks", Tests.bookmark_opened) + open_tool(sc, "VariableManager", "Variable Manager", Tests.variable_manager_opened) + + # 4) Close Node Palette, Node Inspector, Bookmarks and Variable Manager in Script Canvas window + close_tool(sc, "NodePalette", Tests.node_palette_closed_by_end) + close_tool(sc, "NodeInspector", Tests.node_inspector_closed_by_end) + close_tool(sc, "BookmarkDockWidget", Tests.bookmark_closed_by_end) + close_tool(sc, "VariableManager", Tests.variable_manager_closed_by_end) + + # 5) Restore default layout + # Need this step to restore to default in case of test failure + click_menu_option(sc, "Restore Default Layout") + + # 6) Close Script Canvas window + sc.close() + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + + from utils import Report + + Report.start_test(Toggle_ScriptCanvasTools) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/UnDockedPane_CloseSCWindow.py b/AutomatedTesting/Gem/PythonTests/scripting/UnDockedPane_CloseSCWindow.py new file mode 100644 index 0000000000..875f28ec95 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/UnDockedPane_CloseSCWindow.py @@ -0,0 +1,128 @@ +""" +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 ID: C1702825 // C1702831 +Test Case Title: Undocking // Closing script canvas with the pane floating +URLs of the test case: https://testrail.agscollab.com/index.php?/cases/view/1702825 & + https://testrail.agscollab.com/index.php?/cases/view/1702831 +""" + + +# fmt: off +class Tests(): + undock_pane = ("Pane is undocked successfully", "Failed to undock pane") + close_sc_window = ("Script Canvas window is closed", "Failed to close Script Canvas window") + pane_closed = ("Pane is closed successfully", "Failed to close the pane") +# fmt: on + + +def UnDockedPane_CloseSCWindow(): + """ + Summary: + The Script Canvas window is opened with one of the pane undocked. + Verify if undocked pane closes upon closing Script canvas window. + + Expected Behavior: + The undocked pane closes when Script Canvas window closed. + + Test Steps: + 1) Open Script Canvas window (Tools > Script Canvas) + 2) Undock Node Palette pane + 3) Connect to Pane visibility signal emitter to verify pane closed + 4) Close Script Canvas window + 5) Restore default layout + + 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 + """ + + # Helper imports + from utils import Report + from utils import TestHelper as helper + import pyside_utils + + # Open 3D Engine imports + import azlmbr.legacy.general as general + + # Pyside imports + from PySide2 import QtWidgets + + TEST_PANE = "NodePalette" # Chosen most commonly used pane + + def click_menu_option(window, option_text): + action = pyside_utils.find_child_by_pattern(window, {"text": option_text, "type": QtWidgets.QAction}) + action.trigger() + + def find_pane(window, pane_name): + return window.findChild(QtWidgets.QDockWidget, pane_name) + + def on_top_level_changed(): + # This function has test condition always True since it gets emitted only when condition satisfied + Report.result(Tests.undock_pane, True) + + def on_pane_closed(): + # This function has test condition always True since it gets emitted only when condition satisfied + Report.result(Tests.pane_closed, True) + + # Test starts here + general.idle_enable(True) + + # 1) Open Script Canvas window (Tools > Script Canvas) + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) + + # 2) Undock Node Palette pane + # Make sure Node Palette pane is opened + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + pane = find_pane(sc, TEST_PANE) + if not pane.isVisible(): + click_menu_option(sc, "Node Palette") + pane = find_pane(sc, TEST_PANE) # New reference + + # We drag/drop pane over the graph since it doesn't allow docking, so this will undock it + try: + graph = find_pane(sc, "GraphCanvasEditorCentralWidget") + try: + pane.topLevelChanged.connect(on_top_level_changed) + pyside_utils.drag_and_drop(pane, graph) + finally: + pane.topLevelChanged.disconnect(on_top_level_changed) + + # 3) Connect to Pane visibility signal emitter to verify pane closed + # No need to disconnect this since pane widget gets deleted when SC window closed + pane.visibilityChanged.connect(on_pane_closed) + + # 4) Close Script Canvas window + sc.close() + is_sc_visible = helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 2.0) + Report.result(Tests.close_sc_window, not is_sc_visible) + + finally: + # 5) Restore default layout + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + click_menu_option(sc, "Restore Default Layout") + sc.close() + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + + from utils import Report + + Report.start_test(UnDockedPane_CloseSCWindow) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/VariableManager_CreateDeleteVars.py b/AutomatedTesting/Gem/PythonTests/scripting/VariableManager_CreateDeleteVars.py new file mode 100644 index 0000000000..6facc324d4 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/VariableManager_CreateDeleteVars.py @@ -0,0 +1,123 @@ +""" +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 ID: T92564789 +Test Case Title: Each Variable type can be created +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92564789 +Test case ID: T92568873 +Test Case Title: Each Variable type can be deleted +URL of the test case: https://testrail.agscollab.com/index.php?/tests/view/92568873 +""" + + +def VariableManager_CreateDeleteVars(): + """ + Summary: + Each variable type can be created and deleted in variable manager. + + Expected Behavior: + Each variable type can be created and deleted in variable manager. + + Test Steps: + 1) Open Script Canvas window (Tools > Script Canvas) + 2) Get the SC window object + 3) Open Variable Manager if not opened already + 4) Create Graph + 5) Create variable of each type and verify if it is created + 6) Delete each type of variable and verify if it is deleted + 7) Close SC window + + 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 + """ + + from PySide2 import QtWidgets, QtCore, QtTest + + from PySide2.QtCore import Qt + + from utils import TestHelper as helper + + import azlmbr.legacy.general as general + + import pyside_utils + + def generate_test_tuple(var_type, action): + return (f"{var_type} variable is {action}d", f"{var_type} variable is not {action}d") + + # 1) Open Script Canvas window + general.idle_enable(True) + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 10.0) + + # 2) Get the SC window object + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + + # 3) Open Variable Manager if not opened already + if sc.findChild(QtWidgets.QDockWidget, "VariableManager") is None: + action = pyside_utils.find_child_by_pattern(sc, {"text": "Variable Manager", "type": QtWidgets.QAction}) + action.trigger() + variable_manager = sc.findChild(QtWidgets.QDockWidget, "VariableManager") + + # 4) Create Graph + action = pyside_utils.find_child_by_pattern(sc, {"objectName": "action_New_Script", "type": QtWidgets.QAction}) + action.trigger() + + graph_vars = variable_manager.findChild(QtWidgets.QTableView, "graphVariables") + + var_types = ["Boolean", "Color", "EntityID", "Number", "String", "Transform", "Vector2", "Vector3", "Vector4"] + + # 5) Create variable of each type and verify if it is created + for index, var_type in enumerate(var_types): + # Create new variable + add_button = variable_manager.findChild(QtWidgets.QPushButton, "addButton") + add_button.click() # Click on Create Variable button + # Select variable type + table_view = variable_manager.findChild(QtWidgets.QTableView, "variablePalette") + model_index = pyside_utils.find_child_by_pattern(table_view, var_type) + # Click on it to create variable + pyside_utils.item_view_index_mouse_click(table_view, model_index) + # Verify if the variable is created + # NOTE: To check if variable of a type is created, we are checking 1) rowcount + # 2) If we have row with variable "Variable " + # 3) Type of variable, which is next column of the variable name + result = graph_vars.model().rowCount(QtCore.QModelIndex()) == ( + index + 1 + ) # since we added 1 variable, rowcount will increase by 1 + var_mi = pyside_utils.find_child_by_pattern(graph_vars, f"Variable {index+1}") + result = result and (var_mi is not None) and (var_mi.siblingAtColumn(1).data(Qt.DisplayRole) == var_type) + Report.result(generate_test_tuple(var_type, "create"), result) + + # 6) Delete each type of variable and verify if it is deleted + for index, var_type in enumerate(var_types): + # Delete variable and verify if its deleted + # NOTE: To check if variable of a type is deleted, we are checking rowcount + var_mi = pyside_utils.find_child_by_pattern(graph_vars, f"Variable {index+1}") + pyside_utils.item_view_index_mouse_click(graph_vars, var_mi) + QtTest.QTest.keyClick(graph_vars, Qt.Key_Delete, Qt.NoModifier) + # since variable is deleted, rowcount will decrease by 1 + result = graph_vars.model().rowCount(QtCore.QModelIndex()) == (len(var_types) - (index + 1)) + Report.result(generate_test_tuple(var_type, "delete"), result) + + # 7) Close SC window + general.close_pane("Script Canvas") + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + from utils import Report + + Report.start_test(VariableManager_CreateDeleteVars) 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/Registry/C14195074_ScriptCanvas_PostUpdateEvent.setreg b/AutomatedTesting/Registry/C14195074_ScriptCanvas_PostUpdateEvent.setreg_override similarity index 100% rename from AutomatedTesting/Registry/C14195074_ScriptCanvas_PostUpdateEvent.setreg rename to AutomatedTesting/Registry/C14195074_ScriptCanvas_PostUpdateEvent.setreg_override diff --git a/AutomatedTesting/Registry/C14902097_ScriptCanvas_PreUpdateEvent.setreg b/AutomatedTesting/Registry/C14902097_ScriptCanvas_PreUpdateEvent.setreg_override similarity index 100% rename from AutomatedTesting/Registry/C14902097_ScriptCanvas_PreUpdateEvent.setreg rename to AutomatedTesting/Registry/C14902097_ScriptCanvas_PreUpdateEvent.setreg_override diff --git a/AutomatedTesting/Registry/C3510644_Collider_CollisionGroups.setreg b/AutomatedTesting/Registry/C3510644_Collider_CollisionGroups.setreg_override similarity index 100% rename from AutomatedTesting/Registry/C3510644_Collider_CollisionGroups.setreg rename to AutomatedTesting/Registry/C3510644_Collider_CollisionGroups.setreg_override diff --git a/AutomatedTesting/Registry/C4976227_Collider_NewGroup.setreg b/AutomatedTesting/Registry/C4976227_Collider_NewGroup.setreg_override similarity index 100% rename from AutomatedTesting/Registry/C4976227_Collider_NewGroup.setreg rename to AutomatedTesting/Registry/C4976227_Collider_NewGroup.setreg_override diff --git a/AutomatedTesting/Registry/C4976244_Collider_SameGroupSameLayerCollision.setreg b/AutomatedTesting/Registry/C4976244_Collider_SameGroupSameLayerCollision.setreg_override similarity index 100% rename from AutomatedTesting/Registry/C4976244_Collider_SameGroupSameLayerCollision.setreg rename to AutomatedTesting/Registry/C4976244_Collider_SameGroupSameLayerCollision.setreg_override diff --git a/AutomatedTesting/Registry/C4976245_PhysXCollider_CollisionLayerTest.setreg b/AutomatedTesting/Registry/C4976245_PhysXCollider_CollisionLayerTest.setreg_override similarity index 100% rename from AutomatedTesting/Registry/C4976245_PhysXCollider_CollisionLayerTest.setreg rename to AutomatedTesting/Registry/C4976245_PhysXCollider_CollisionLayerTest.setreg_override diff --git a/AutomatedTesting/Registry/C4982593_PhysXCollider_CollisionLayer.setreg b/AutomatedTesting/Registry/C4982593_PhysXCollider_CollisionLayer.setreg_override similarity index 100% rename from AutomatedTesting/Registry/C4982593_PhysXCollider_CollisionLayer.setreg rename to AutomatedTesting/Registry/C4982593_PhysXCollider_CollisionLayer.setreg_override diff --git a/AutomatedTesting/ScriptCanvas/OnEntityActivatedScripts/activator.scriptcanvas b/AutomatedTesting/ScriptCanvas/OnEntityActivatedScripts/activator.scriptcanvas new file mode 100644 index 0000000000..89d86f0d54 --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/OnEntityActivatedScripts/activator.scriptcanvas @@ -0,0 +1,778 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutomatedTesting/ScriptCanvas/OnEntityActivatedScripts/controller.scriptcanvas b/AutomatedTesting/ScriptCanvas/OnEntityActivatedScripts/controller.scriptcanvas new file mode 100644 index 0000000000..83a899798e --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/OnEntityActivatedScripts/controller.scriptcanvas @@ -0,0 +1,1865 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutomatedTesting/ScriptCanvas/OnEntityActivatedScripts/deactivator.scriptcanvas b/AutomatedTesting/ScriptCanvas/OnEntityActivatedScripts/deactivator.scriptcanvas new file mode 100644 index 0000000000..ca98ee303a --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/OnEntityActivatedScripts/deactivator.scriptcanvas @@ -0,0 +1,766 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutomatedTesting/ScriptCanvas/ScriptCanvas_TwoComponents0.scriptcanvas b/AutomatedTesting/ScriptCanvas/ScriptCanvas_TwoComponents0.scriptcanvas new file mode 100644 index 0000000000..af3afead19 --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/ScriptCanvas_TwoComponents0.scriptcanvas @@ -0,0 +1,365 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutomatedTesting/ScriptCanvas/ScriptCanvas_TwoComponents1.scriptcanvas b/AutomatedTesting/ScriptCanvas/ScriptCanvas_TwoComponents1.scriptcanvas new file mode 100644 index 0000000000..0a16717520 --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/ScriptCanvas_TwoComponents1.scriptcanvas @@ -0,0 +1,365 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutomatedTesting/ScriptCanvas/T92563191_test.scriptcanvas b/AutomatedTesting/ScriptCanvas/T92563191_test.scriptcanvas new file mode 100644 index 0000000000..f0797739a5 --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/T92563191_test.scriptcanvas @@ -0,0 +1,750 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutomatedTesting/ScriptCanvas/T92567320.scriptcanvas b/AutomatedTesting/ScriptCanvas/T92567320.scriptcanvas new file mode 100644 index 0000000000..d509442435 --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/T92567320.scriptcanvas @@ -0,0 +1,1266 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutomatedTesting/ScriptCanvas/T92567321A.scriptcanvas b/AutomatedTesting/ScriptCanvas/T92567321A.scriptcanvas new file mode 100644 index 0000000000..644a5daa17 --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/T92567321A.scriptcanvas @@ -0,0 +1,778 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutomatedTesting/ScriptCanvas/T92567321B.scriptcanvas b/AutomatedTesting/ScriptCanvas/T92567321B.scriptcanvas new file mode 100644 index 0000000000..54c83c2933 --- /dev/null +++ b/AutomatedTesting/ScriptCanvas/T92567321B.scriptcanvas @@ -0,0 +1,880 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutomatedTesting/TestAssets/T92567320.scriptevents b/AutomatedTesting/TestAssets/T92567320.scriptevents new file mode 100644 index 0000000000..63cf20f2d0 --- /dev/null +++ b/AutomatedTesting/TestAssets/T92567320.scriptevents @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutomatedTesting/TestAssets/T92567321.scriptevents b/AutomatedTesting/TestAssets/T92567321.scriptevents new file mode 100644 index 0000000000..6f9308ad02 --- /dev/null +++ b/AutomatedTesting/TestAssets/T92567321.scriptevents @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/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/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/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/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/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/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/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/CryEdit.cpp b/Code/Sandbox/Editor/CryEdit.cpp index 9ad57d6581..b88aee8bd9 100644 --- a/Code/Sandbox/Editor/CryEdit.cpp +++ b/Code/Sandbox/Editor/CryEdit.cpp @@ -3200,7 +3200,7 @@ CCryEditApp::ECreateLevelResult CCryEditApp::CreateLevel(const QString& levelNam m_bIsExportingLegacyData = false; } - GetIEditor()->GetGameEngine()->LoadLevel(GetIEditor()->GetGameEngine()->GetMissionName(), true, true); + GetIEditor()->GetGameEngine()->LoadLevel(true, true); GetIEditor()->GetSystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_START, 0, 0); GetIEditor()->GetSystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_END, 0, 0); diff --git a/Code/Sandbox/Editor/CryEditDoc.cpp b/Code/Sandbox/Editor/CryEditDoc.cpp index 557341f28c..ffcbd00a5f 100644 --- a/Code/Sandbox/Editor/CryEditDoc.cpp +++ b/Code/Sandbox/Editor/CryEditDoc.cpp @@ -459,6 +459,11 @@ void CCryEditDoc::Load(TDocMultiArchive& arrXmlAr, const QString& szFilename) Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData); } + { + CAutoLogTime logtime("Game Engine level load"); + GetIEditor()->GetGameEngine()->LoadLevel(true, true); + } + if (!isPrefabEnabled) { ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index d9344746a2..e208e83067 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -161,6 +161,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 +1099,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); @@ -1234,6 +1209,8 @@ void EditorViewportWidget::SetViewportId(int id) m_renderViewport->GetControllerList()->Add(AZStd::make_shared()); } + m_renderViewport->SetViewportSettings(&m_editorViewportSettings); + UpdateScene(); if (m_pPrimaryViewport == this) @@ -2853,4 +2830,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/GameEngine.cpp b/Code/Sandbox/Editor/GameEngine.cpp index 6c1bef7a68..073684eaad 100644 --- a/Code/Sandbox/Editor/GameEngine.cpp +++ b/Code/Sandbox/Editor/GameEngine.cpp @@ -539,19 +539,12 @@ void CGameEngine::SetLevelPath(const QString& path) } } -void CGameEngine::SetMissionName(const QString& mission) -{ - m_missionName = mission; -} - bool CGameEngine::LoadLevel( - const QString& mission, [[maybe_unused]] bool bDeleteAIGraph, bool bReleaseResources) { LOADING_TIME_PROFILE_SECTION(GetIEditor()->GetSystem()); m_bLevelLoaded = false; - m_missionName = mission; CLogFile::FormatLine("Loading map '%s' into engine...", m_levelPath.toUtf8().data()); // Switch the current directory back to the Primary CD folder first. // The engine might have trouble to find some files when the current @@ -600,7 +593,7 @@ bool CGameEngine::LoadLevel( bool CGameEngine::ReloadLevel() { - if (!LoadLevel(GetMissionName(), false, false)) + if (!LoadLevel(false, false)) { return false; } diff --git a/Code/Sandbox/Editor/GameEngine.h b/Code/Sandbox/Editor/GameEngine.h index c660f971f2..b59e2c9ac8 100644 --- a/Code/Sandbox/Editor/GameEngine.h +++ b/Code/Sandbox/Editor/GameEngine.h @@ -89,7 +89,6 @@ public: //! Load new terrain level into 3d engine. //! Also load AI triangulation for this level. bool LoadLevel( - const QString& mission, bool bDeleteAIGraph, bool bReleaseResources); //!* Reload level if it was already loaded. @@ -107,14 +106,10 @@ public: bool IsLevelLoaded() const { return m_bLevelLoaded; }; //! Assign new level path name. void SetLevelPath(const QString& path); - //! Assign new current mission name. - void SetMissionName(const QString& mission); //! Return name of currently loaded level. const QString& GetLevelName() const { return m_levelName; }; //! Return extension of currently loaded level. const QString& GetLevelExtension() const { return m_levelExtension; }; - //! Return name of currently active mission. - const QString& GetMissionName() const { return m_missionName; }; //! Get fully specified level path. const QString& GetLevelPath() const { return m_levelPath; }; //! Query if engine is in game mode. @@ -172,7 +167,6 @@ private: CLogFile m_logFile; QString m_levelName; QString m_levelExtension; - QString m_missionName; QString m_levelPath; QString m_MOD; bool m_bLevelLoaded; 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/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/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.cpp b/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.cpp index c2aac4e943..73bc075deb 100644 --- a/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.cpp +++ b/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.cpp @@ -20,13 +20,18 @@ namespace AssetBundler { const char* DateTimeFormat = "hh:mm:ss MMM dd, yyyy"; + const char* ReadOnlyFileErrorMessage = "File (%s) is Read-Only. Please check your version control and try again."; AssetBundlerAbstractFileTableModel::AssetBundlerAbstractFileTableModel(QObject* parent) : QAbstractTableModel(parent) { } - void AssetBundlerAbstractFileTableModel::Reload(const char* fileExtension, const QSet& watchedFolders, const QSet& watchedFiles, const AZStd::unordered_map& pathToProjectNameMap) + void AssetBundlerAbstractFileTableModel::Reload( + const char* fileExtension, + const QSet& watchedFolders, + const QSet& watchedFiles, + const AZStd::unordered_map& pathToProjectNameMap) { AZStd::vector keysToRemove = m_fileListKeys; @@ -49,7 +54,9 @@ namespace AssetBundler // If a project name is already specified, then the associated file is a default file LoadFile(absolutePath, projectName, !projectName.empty()); - keysToRemove.erase(AZStd::remove(keysToRemove.begin(), keysToRemove.end(), AssetBundler::GenerateKeyFromAbsolutePath(absolutePath)), keysToRemove.end()); + keysToRemove.erase( + AZStd::remove(keysToRemove.begin(), keysToRemove.end(), AssetBundler::GenerateKeyFromAbsolutePath(absolutePath)), + keysToRemove.end()); } } @@ -63,7 +70,9 @@ namespace AssetBundler // If a project name is already specified, then the associated file is a default file LoadFile(absolutePath, projectName, !projectName.empty()); - keysToRemove.erase(AZStd::remove(keysToRemove.begin(), keysToRemove.end(), AssetBundler::GenerateKeyFromAbsolutePath(absolutePath)), keysToRemove.end()); + keysToRemove.erase( + AZStd::remove(keysToRemove.begin(), keysToRemove.end(), AssetBundler::GenerateKeyFromAbsolutePath(absolutePath)), + keysToRemove.end()); } } @@ -74,7 +83,9 @@ namespace AssetBundler } } - void AssetBundlerAbstractFileTableModel::ReloadFiles(const AZStd::vector& absoluteFilePathList, AZStd::unordered_map pathToProjectNameMap) + void AssetBundlerAbstractFileTableModel::ReloadFiles( + const AZStd::vector& absoluteFilePathList, + AZStd::unordered_map pathToProjectNameMap) { for (const AZStd::string& absoluteFilePath : absoluteFilePathList) { diff --git a/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.h b/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.h index cfe3e3f241..6c7ae81254 100644 --- a/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.h +++ b/Code/Tools/AssetBundler/source/models/AssetBundlerAbstractFileTableModel.h @@ -30,6 +30,7 @@ namespace AssetBundler { extern const char* DateTimeFormat; + extern const char* ReadOnlyFileErrorMessage; //! Provides an abstract model that can be subclassed to create table models used to store information about files found on-disk. class AssetBundlerAbstractFileTableModel @@ -47,9 +48,15 @@ namespace AssetBundler ////////////////////////////////////////////////////////////////////////// // Pure virtual functions - virtual AZStd::vector CreateNewFiles(const AZStd::string& absoluteFilePath, const AzFramework::PlatformFlags& platforms, const QString& project = QString()) = 0; + virtual AZStd::vector CreateNewFiles( + const AZStd::string& absoluteFilePath, + const AzFramework::PlatformFlags& platforms, + const QString& project = QString()) = 0; virtual bool DeleteFile(const QModelIndex& index) = 0; - virtual void LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& projectName = "", bool isDefaultFile = false) = 0; + virtual void LoadFile( + const AZStd::string& absoluteFilePath, + const AZStd::string& projectName = "", + bool isDefaultFile = false) = 0; virtual bool WriteToDisk(const AZStd::string& key) = 0; //! Returns the absolute path of the file at the given index on success, returns an empty string on failure. @@ -60,9 +67,15 @@ namespace AssetBundler ////////////////////////////////////////////////////////////////////////// //! Reload all the data based on the watched folders and files - virtual void Reload(const char* fileExtension, const QSet& watchedFolders, const QSet& watchedFiles = QSet(), const AZStd::unordered_map& pathToProjectNameMap = AZStd::unordered_map()); + virtual void Reload( + const char* fileExtension, + const QSet& watchedFolders, + const QSet& watchedFiles = QSet(), + const AZStd::unordered_map& pathToProjectNameMap = AZStd::unordered_map()); - virtual void ReloadFiles(const AZStd::vector& absoluteFilePathList, AZStd::unordered_map pathToProjectNameMap = AZStd::unordered_map()); + virtual void ReloadFiles( + const AZStd::vector& absoluteFilePathList, + AZStd::unordered_map pathToProjectNameMap = AZStd::unordered_map()); bool Save(const QModelIndex& selectedIndex); diff --git a/Code/Tools/AssetBundler/source/models/AssetListFileTableModel.cpp b/Code/Tools/AssetBundler/source/models/AssetListFileTableModel.cpp index e4bb7b6ca2..a2502c73a5 100644 --- a/Code/Tools/AssetBundler/source/models/AssetListFileTableModel.cpp +++ b/Code/Tools/AssetBundler/source/models/AssetListFileTableModel.cpp @@ -87,12 +87,15 @@ namespace AssetBundler { if (AZ::IO::FileIOBase::GetInstance()->IsReadOnly(absolutePath)) { + AZ_Error(AssetBundler::AppWindowName, false, ReadOnlyFileErrorMessage, absolutePath); return false; } auto deleteResult = AZ::IO::FileIOBase::GetInstance()->Remove(absolutePath); if (!deleteResult) { + AZ_Error(AssetBundler::AppWindowName, false, + "Unable to delete (%s). Result code: %u", absolutePath, deleteResult.GetResultCode()); return false; } } @@ -104,7 +107,10 @@ namespace AssetBundler return true; } - void AssetListFileTableModel::LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& /*projectName*/, bool /*isDefaultFile*/) + void AssetListFileTableModel::LoadFile( + const AZStd::string& absoluteFilePath, + const AZStd::string& /*projectName*/, + bool /*isDefaultFile*/) { AZStd::string fullFileName; AzFramework::StringFunc::Path::GetFullFileName(absoluteFilePath.c_str(), fullFileName); diff --git a/Code/Tools/AssetBundler/source/models/AssetListFileTableModel.h b/Code/Tools/AssetBundler/source/models/AssetListFileTableModel.h index 61f1455130..699122a1ea 100644 --- a/Code/Tools/AssetBundler/source/models/AssetListFileTableModel.h +++ b/Code/Tools/AssetBundler/source/models/AssetListFileTableModel.h @@ -56,7 +56,10 @@ namespace AssetBundler ////////////////////////////////////////////////////////////////////////// // AssetBundlerAbstractFileTableModel overrides - AZStd::vector CreateNewFiles(const AZStd::string& /*absoluteFilePath*/, const AzFramework::PlatformFlags& /*platforms*/, const QString& /*project*/) override { return {}; } + AZStd::vector CreateNewFiles( + const AZStd::string& /*absoluteFilePath*/, + const AzFramework::PlatformFlags& /*platforms*/, + const QString& /*project*/) override { return {}; } bool DeleteFile(const QModelIndex& index) override; void LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& projectName = "", bool isDefaultFile = false) override; bool WriteToDisk(const AZStd::string& key) override; diff --git a/Code/Tools/AssetBundler/source/models/AssetListTableModel.h b/Code/Tools/AssetBundler/source/models/AssetListTableModel.h index 94500f520f..f05fa0572b 100644 --- a/Code/Tools/AssetBundler/source/models/AssetListTableModel.h +++ b/Code/Tools/AssetBundler/source/models/AssetListTableModel.h @@ -23,7 +23,10 @@ namespace AssetBundler : public QAbstractTableModel { public: - explicit AssetListTableModel(QObject* parent = nullptr, const AZStd::string& absolutePath = AZStd::string(), const AZStd::string& platform = ""); + explicit AssetListTableModel( + QObject* parent = nullptr, + const AZStd::string& absolutePath = AZStd::string(), + const AZStd::string& platform = ""); virtual ~AssetListTableModel() {} AZStd::shared_ptr GetSeedListManager() { return m_seedListManager; } diff --git a/Code/Tools/AssetBundler/source/models/BundleFileListModel.cpp b/Code/Tools/AssetBundler/source/models/BundleFileListModel.cpp index d7c0e93279..a2bb6ef91f 100644 --- a/Code/Tools/AssetBundler/source/models/BundleFileListModel.cpp +++ b/Code/Tools/AssetBundler/source/models/BundleFileListModel.cpp @@ -73,14 +73,15 @@ namespace AssetBundler { if (AZ::IO::FileIOBase::GetInstance()->IsReadOnly(absolutePath)) { - AZ_Error(AssetBundler::AppWindowName, false, "File (%s) is Read-Only. Please check your version control and try again.", absolutePath); + AZ_Error(AssetBundler::AppWindowName, false, ReadOnlyFileErrorMessage, absolutePath); return false; } auto deleteResult = AZ::IO::FileIOBase::GetInstance()->Remove(absolutePath); if (!deleteResult) { - AZ_Error(AssetBundler::AppWindowName, false, "Unable to delete: %s", absolutePath); + AZ_Error(AssetBundler::AppWindowName, false, + "Unable to delete (%s). Result code: %u", absolutePath, deleteResult.GetResultCode()); return false; } } @@ -175,7 +176,10 @@ namespace AssetBundler return Column::ColumnFileCreationTime; } - void BundleFileListModel::LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& /*projectName*/, bool /*isDefaultFile*/) + void BundleFileListModel::LoadFile( + const AZStd::string& absoluteFilePath, + const AZStd::string& /*projectName*/, + bool /*isDefaultFile*/) { AZStd::string key = AssetBundler::GenerateKeyFromAbsolutePath(absoluteFilePath); diff --git a/Code/Tools/AssetBundler/source/models/BundleFileListModel.h b/Code/Tools/AssetBundler/source/models/BundleFileListModel.h index 13e5838c57..320c2b2d59 100644 --- a/Code/Tools/AssetBundler/source/models/BundleFileListModel.h +++ b/Code/Tools/AssetBundler/source/models/BundleFileListModel.h @@ -43,7 +43,10 @@ namespace AssetBundler explicit BundleFileListModel(); virtual ~BundleFileListModel() {} - AZStd::vector CreateNewFiles(const AZStd::string& /*absoluteFilePath*/, const AzFramework::PlatformFlags& /*platforms*/, const QString& /*project*/) override { return {}; } + AZStd::vector CreateNewFiles( + const AZStd::string& /*absoluteFilePath*/, + const AzFramework::PlatformFlags& /*platforms*/, + const QString& /*project*/) override { return {}; } bool DeleteFile(const QModelIndex& index) override; void LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& projectName = "", bool isDefaultFile = false) override; bool WriteToDisk(const AZStd::string& /*key*/) override { return true; } diff --git a/Code/Tools/AssetBundler/source/models/RulesFileTableModel.cpp b/Code/Tools/AssetBundler/source/models/RulesFileTableModel.cpp index c7ae384d6e..b452dee6bf 100644 --- a/Code/Tools/AssetBundler/source/models/RulesFileTableModel.cpp +++ b/Code/Tools/AssetBundler/source/models/RulesFileTableModel.cpp @@ -67,7 +67,10 @@ namespace AssetBundler { } - AZStd::vector RulesFileTableModel::CreateNewFiles(const AZStd::string& absoluteFilePath, const AzFramework::PlatformFlags& /*platforms*/, const QString& /*project*/) + AZStd::vector RulesFileTableModel::CreateNewFiles( + const AZStd::string& absoluteFilePath, + const AzFramework::PlatformFlags& /*platforms*/, + const QString& /*project*/) { if (absoluteFilePath.empty()) { @@ -116,14 +119,16 @@ namespace AssetBundler // Remove file from disk if (AZ::IO::FileIOBase::GetInstance()->IsReadOnly(rulesFileInfo->m_absolutePath.c_str())) { - AZ_Error(AssetBundler::AppWindowName, false, "File (%s) is Read-Only. Please check your version control and try again.", rulesFileInfo->m_absolutePath.c_str()); + AZ_Error(AssetBundler::AppWindowName, false, ReadOnlyFileErrorMessage, rulesFileInfo->m_absolutePath.c_str()); return false; } auto deleteResult = AZ::IO::FileIOBase::GetInstance()->Remove(rulesFileInfo->m_absolutePath.c_str()); if (!deleteResult) { - AZ_Error(AssetBundler::AppWindowName, false, "Unable to delete: %s", rulesFileInfo->m_absolutePath.c_str()); + AZ_Error(AssetBundler::AppWindowName, false, + "Unable to delete (%s). Result code: %u", rulesFileInfo->m_absolutePath.c_str(), + deleteResult.GetResultCode()); return false; } @@ -134,7 +139,10 @@ namespace AssetBundler return true; } - void RulesFileTableModel::LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& /*projectName*/, bool /*isDefaultFile*/) + void RulesFileTableModel::LoadFile( + const AZStd::string& absoluteFilePath, + const AZStd::string& /*projectName*/, + bool /*isDefaultFile*/) { // Get the file name without the extension for display purposes AZStd::string fileName(absoluteFilePath); @@ -145,7 +153,8 @@ namespace AssetBundler auto fileInfoIt = m_rulesFileInfoMap.find(key); if (fileInfoIt != m_rulesFileInfoMap.end() && fileInfoIt->second->m_hasUnsavedChanges) { - AZ_Warning(AssetBundler::AppWindowName, false, "Rules File %s has unsaved changes and couldn't be reloaded", absoluteFilePath.c_str()); + AZ_Warning(AssetBundler::AppWindowName, false, + "Rules File %s has unsaved changes and couldn't be reloaded", absoluteFilePath.c_str()); return; } diff --git a/Code/Tools/AssetBundler/source/models/RulesFileTableModel.h b/Code/Tools/AssetBundler/source/models/RulesFileTableModel.h index a9931a9819..7808fc2479 100644 --- a/Code/Tools/AssetBundler/source/models/RulesFileTableModel.h +++ b/Code/Tools/AssetBundler/source/models/RulesFileTableModel.h @@ -48,7 +48,10 @@ namespace AssetBundler RulesFileTableModel(); virtual ~RulesFileTableModel() {} - AZStd::vector CreateNewFiles(const AZStd::string& absoluteFilePath, const AzFramework::PlatformFlags& platforms = AzFramework::PlatformFlags::Platform_NONE, const QString& project = QString()) override; + AZStd::vector CreateNewFiles( + const AZStd::string& absoluteFilePath, + const AzFramework::PlatformFlags& platforms = AzFramework::PlatformFlags::Platform_NONE, + const QString& project = QString()) override; bool DeleteFile(const QModelIndex& index) override; diff --git a/Code/Tools/AssetBundler/source/models/SeedListFileTableModel.cpp b/Code/Tools/AssetBundler/source/models/SeedListFileTableModel.cpp index 1e7ec36bac..36622c809d 100644 --- a/Code/Tools/AssetBundler/source/models/SeedListFileTableModel.cpp +++ b/Code/Tools/AssetBundler/source/models/SeedListFileTableModel.cpp @@ -91,12 +91,19 @@ namespace AssetBundler m_seedTabWidget = nullptr; } - void SeedListFileTableModel::AddDefaultSeedsToInMemoryList(const AZStd::vector& defaultSeeds, const char* projectName, const AzFramework::PlatformFlags& platforms) + void SeedListFileTableModel::AddDefaultSeedsToInMemoryList( + const AZStd::vector& defaultSeeds, + const char* projectName, + const AzFramework::PlatformFlags& platforms) { - m_inMemoryDefaultSeedList.reset(new SeedListFileInfo(m_inMemoryDefaultSeedListKey, tr("DefaultSeeds"), QString(projectName), false, true, defaultSeeds, platforms)); + m_inMemoryDefaultSeedList.reset( + new SeedListFileInfo(m_inMemoryDefaultSeedListKey, tr("DefaultSeeds"), QString(projectName), false, true, defaultSeeds, platforms)); } - AZStd::vector SeedListFileTableModel::CreateNewFiles(const AZStd::string& absoluteFilePath, const AzFramework::PlatformFlags& /*platforms*/, const QString& project) + AZStd::vector SeedListFileTableModel::CreateNewFiles( + const AZStd::string& absoluteFilePath, + const AzFramework::PlatformFlags& /*platforms*/, + const QString& project) { if (absoluteFilePath.empty()) { @@ -111,7 +118,8 @@ namespace AssetBundler // Create a Seed List File and save it to disk AZStd::string key = AssetBundler::GenerateKeyFromAbsolutePath(absoluteFilePath); - AZStd::shared_ptr newSeedListFile = AZStd::make_shared(absoluteFilePath, QString(fileName.c_str()), project, false); + AZStd::shared_ptr newSeedListFile = + AZStd::make_shared(absoluteFilePath, QString(fileName.c_str()), project, false); newSeedListFile->m_seedListModel->SetHasUnsavedChanges(true); bool saveResult = newSeedListFile->SaveSeedFile(); if (!saveResult) @@ -151,14 +159,15 @@ namespace AssetBundler { if (AZ::IO::FileIOBase::GetInstance()->IsReadOnly(absolutePath)) { - AZ_Error(AssetBundler::AppWindowName, false, "File (%s) is Read-Only. Please check your version control and try again.", absolutePath); + AZ_Error(AssetBundler::AppWindowName, false, ReadOnlyFileErrorMessage, absolutePath); return false; } auto deleteResult = AZ::IO::FileIOBase::GetInstance()->Remove(absolutePath); if (!deleteResult) { - AZ_Error(AssetBundler::AppWindowName, false, "Unable to delete: %s", absolutePath); + AZ_Error(AssetBundler::AppWindowName, false, + "Unable to delete (%s). Result code: %u", absolutePath, deleteResult.GetResultCode()); return false; } } @@ -170,7 +179,11 @@ namespace AssetBundler return true; } - void SeedListFileTableModel::Reload(const char* fileExtension, const QSet& watchedFolders, const QSet& watchedFiles, const AZStd::unordered_map& pathToProjectNameMap) + void SeedListFileTableModel::Reload( + const char* fileExtension, + const QSet& watchedFolders, + const QSet& watchedFiles, + const AZStd::unordered_map& pathToProjectNameMap) { // Load in the Seed List files from disk AssetBundlerAbstractFileTableModel::Reload(fileExtension, watchedFolders, watchedFiles, pathToProjectNameMap); @@ -191,7 +204,8 @@ namespace AssetBundler auto fileInfoIt = m_seedListFileInfoMap.find(key); if (fileInfoIt != m_seedListFileInfoMap.end() && fileInfoIt->second->HasUnsavedChanges()) { - AZ_Warning(AssetBundler::AppWindowName, false, "Seed List File %s has unsaved changes and couldn't be reloaded", absoluteFilePath.c_str()); + AZ_Warning(AssetBundler::AppWindowName, false, + "Seed List File %s has unsaved changes and couldn't be reloaded", absoluteFilePath.c_str()); return; } @@ -208,7 +222,8 @@ namespace AssetBundler projectNameOnDisplay = outcome.TakeValue(); } - m_seedListFileInfoMap[key].reset(new SeedListFileInfo(absoluteFilePath, QString(fileName.c_str()), QString(projectNameOnDisplay.c_str()), true, isDefaultFile)); + m_seedListFileInfoMap[key].reset( + new SeedListFileInfo(absoluteFilePath, QString(fileName.c_str()), QString(projectNameOnDisplay.c_str()), true, isDefaultFile)); AddFileKey(key); } @@ -241,7 +256,9 @@ namespace AssetBundler emit dataChanged(firstIndex, lastIndex, { Qt::CheckStateRole }); } - AZStd::vector SeedListFileTableModel::GenerateAssetLists(const AZStd::string& absoluteFilePath, const AzFramework::PlatformFlags& platforms) + AZStd::vector SeedListFileTableModel::GenerateAssetLists( + const AZStd::string& absoluteFilePath, + const AzFramework::PlatformFlags& platforms) { if (!m_checkedSeedListFiles.size()) { @@ -280,7 +297,8 @@ namespace AssetBundler AZStd::vector createdFiles; for (const auto& platformIndex : AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(platforms)) { - AZStd::string platformSpecificCachePath = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(platformIndex); + AZStd::string platformSpecificCachePath = + AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(platformIndex); AzFramework::StringFunc::Path::StripFullName(platformSpecificCachePath); FilePath platformSpecificPath(absoluteFilePath, AZStd::string(AzFramework::PlatformHelper::GetPlatformName(platformIndex))); @@ -304,7 +322,10 @@ namespace AssetBundler return seedFileInfoOutcome.GetValue()->m_seedListModel; } - bool SeedListFileTableModel::SetSeedPlatforms(const QModelIndex& seedFileIndex, const QModelIndex& seedIndex, const AzFramework::PlatformFlags& platforms) + bool SeedListFileTableModel::SetSeedPlatforms( + const QModelIndex& seedFileIndex, + const QModelIndex& seedIndex, + const AzFramework::PlatformFlags& platforms) { AZStd::string key = GetFileKey(seedFileIndex); if (key.empty()) @@ -334,7 +355,10 @@ namespace AssetBundler return true; } - bool SeedListFileTableModel::AddSeed(const QModelIndex& seedFileIndex, const AZStd::string& seedRelativePath, const AzFramework::PlatformFlags& platforms) + bool SeedListFileTableModel::AddSeed( + const QModelIndex& seedFileIndex, + const AZStd::string& seedRelativePath, + const AzFramework::PlatformFlags& platforms) { AZStd::string key = GetFileKey(seedFileIndex); if (key.empty()) diff --git a/Code/Tools/AssetBundler/source/models/SeedListFileTableModel.h b/Code/Tools/AssetBundler/source/models/SeedListFileTableModel.h index 3bd8fbd600..9adac6a64b 100644 --- a/Code/Tools/AssetBundler/source/models/SeedListFileTableModel.h +++ b/Code/Tools/AssetBundler/source/models/SeedListFileTableModel.h @@ -71,15 +71,28 @@ namespace AssetBundler explicit SeedListFileTableModel(SeedTabWidget* parentSeedTabWidget); virtual ~SeedListFileTableModel(); - void AddDefaultSeedsToInMemoryList(const AZStd::vector& defaultSeeds, const char* projectName, const AzFramework::PlatformFlags& platforms); + void AddDefaultSeedsToInMemoryList( + const AZStd::vector& defaultSeeds, + const char* projectName, + const AzFramework::PlatformFlags& platforms); - AZStd::vector CreateNewFiles(const AZStd::string& absoluteFilePath, const AzFramework::PlatformFlags& platforms, const QString& project) override; + AZStd::vector CreateNewFiles( + const AZStd::string& absoluteFilePath, + const AzFramework::PlatformFlags& platforms, + const QString& project) override; bool DeleteFile(const QModelIndex& index) override; - void Reload(const char* fileExtension, const QSet& watchedFolders, const QSet& watchedFiles = QSet(), const AZStd::unordered_map& pathToProjectNameMap = AZStd::unordered_map()) override; + void Reload( + const char* fileExtension, + const QSet& watchedFolders, + const QSet& watchedFiles = QSet(), + const AZStd::unordered_map& pathToProjectNameMap = AZStd::unordered_map()) override; - void LoadFile(const AZStd::string& absoluteFilePath, const AZStd::string& projectName = "", bool isDefaultFile = false) override; + void LoadFile( + const AZStd::string& absoluteFilePath, + const AZStd::string& projectName = "", + bool isDefaultFile = false) override; void SelectDefaultSeedLists(bool setSelected); diff --git a/Code/Tools/AssetBundler/source/models/SeedListTableModel.cpp b/Code/Tools/AssetBundler/source/models/SeedListTableModel.cpp index fc04686436..8b721d961c 100644 --- a/Code/Tools/AssetBundler/source/models/SeedListTableModel.cpp +++ b/Code/Tools/AssetBundler/source/models/SeedListTableModel.cpp @@ -38,7 +38,11 @@ namespace AssetBundler ////////////////////////////////////////////////////////////////////////////////////////////////// // SeedListTableModel ////////////////////////////////////////////////////////////////////////////////////////////////// - SeedListTableModel::SeedListTableModel(QObject* parent, const AZStd::string& absolutePath, const AZStd::vector& defaultSeeds, const AzFramework::PlatformFlags& platforms) + SeedListTableModel::SeedListTableModel( + QObject* parent, + const AZStd::string& absolutePath, + const AZStd::vector& defaultSeeds, + const AzFramework::PlatformFlags& platforms) : QAbstractTableModel(parent) { m_seedListManager.reset(new AzToolsFramework::AssetSeedManager()); @@ -66,7 +70,10 @@ namespace AssetBundler QString platformList; for (const auto& seed : m_seedListManager->GetAssetSeedList()) { - assetInfo = AzToolsFramework::AssetSeedManager::GetAssetInfoById(seed.m_assetId, AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(seed.m_platformFlags)[0], absolutePath); + assetInfo = AzToolsFramework::AssetSeedManager::GetAssetInfoById( + seed.m_assetId, + AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(seed.m_platformFlags)[0], + absolutePath); platformList = QString(m_seedListManager->GetReadablePlatformList(seed).c_str()); m_additionalSeedInfoMap[seed.m_assetId].reset(new AdditionalSeedInfo(assetInfo.m_relativePath.c_str(), platformList)); @@ -126,7 +133,8 @@ namespace AssetBundler AZ_Error(AssetBundler::AppWindowName, false, "Unable to find additional Seed info"); return false; } - additionalSeedInfo->second->m_platformList = QString(AzFramework::PlatformHelper::GetCommaSeparatedPlatformList(platforms).c_str()); + additionalSeedInfo->second->m_platformList = + QString(AzFramework::PlatformHelper::GetCommaSeparatedPlatformList(platforms).c_str()); SetHasUnsavedChanges(true); @@ -140,7 +148,8 @@ namespace AssetBundler bool SeedListTableModel::AddSeed(const AZStd::string& seedRelativePath, const AzFramework::PlatformFlags& platforms) { - AZStd::pair addSeedsResult = m_seedListManager->AddSeedAssetForValidPlatforms(seedRelativePath, platforms); + AZStd::pair addSeedsResult = + m_seedListManager->AddSeedAssetForValidPlatforms(seedRelativePath, platforms); if (!addSeedsResult.first.IsValid() || addSeedsResult.second == AzFramework::PlatformFlags::Platform_NONE) { diff --git a/Code/Tools/AssetBundler/source/models/SeedListTableModel.h b/Code/Tools/AssetBundler/source/models/SeedListTableModel.h index b8cbe95201..37d52717a7 100644 --- a/Code/Tools/AssetBundler/source/models/SeedListTableModel.h +++ b/Code/Tools/AssetBundler/source/models/SeedListTableModel.h @@ -36,7 +36,11 @@ namespace AssetBundler : public QAbstractTableModel { public: - explicit SeedListTableModel(QObject* parent = nullptr, const AZStd::string& absolutePath = AZStd::string(), const AZStd::vector& defaultSeeds = AZStd::vector(), const AzFramework::PlatformFlags& platforms = AzFramework::PlatformFlags::Platform_NONE); + explicit SeedListTableModel( + QObject* parent = nullptr, + const AZStd::string& absolutePath = AZStd::string(), + const AZStd::vector& defaultSeeds = AZStd::vector(), + const AzFramework::PlatformFlags& platforms = AzFramework::PlatformFlags::Platform_NONE); virtual ~SeedListTableModel() {} AZStd::shared_ptr GetSeedListManager() { return m_seedListManager; } diff --git a/Code/Tools/AssetBundler/source/ui/AddSeedDialog.cpp b/Code/Tools/AssetBundler/source/ui/AddSeedDialog.cpp index be156efde2..214fe7e850 100644 --- a/Code/Tools/AssetBundler/source/ui/AddSeedDialog.cpp +++ b/Code/Tools/AssetBundler/source/ui/AddSeedDialog.cpp @@ -22,7 +22,10 @@ const char QtRelativePathPrefix[] = "../"; namespace AssetBundler { - AddSeedDialog::AddSeedDialog(QWidget* parent, const AzFramework::PlatformFlags& enabledPlatforms, const AZStd::string& platformSpecificCachePath) + AddSeedDialog::AddSeedDialog( + QWidget* parent, + const AzFramework::PlatformFlags& enabledPlatforms, + const AZStd::string& platformSpecificCachePath) : QDialog(parent) , m_platformSpecificCachePath(platformSpecificCachePath.c_str()) { @@ -35,7 +38,10 @@ namespace AssetBundler // Set up Platform selection m_ui->platformSelectionWidget->Init(enabledPlatforms); - connect(m_ui->platformSelectionWidget, &PlatformSelectionWidget::PlatformsSelected, this, &AddSeedDialog::OnPlatformSelectionChanged); + connect(m_ui->platformSelectionWidget, + &PlatformSelectionWidget::PlatformsSelected, + this, + &AddSeedDialog::OnPlatformSelectionChanged); // Set up Cancel and Create New File buttons m_ui->addSeedButton->setEnabled(false); diff --git a/Code/Tools/AssetBundler/source/ui/AddSeedDialog.h b/Code/Tools/AssetBundler/source/ui/AddSeedDialog.h index fed6b031d1..7699f0a820 100644 --- a/Code/Tools/AssetBundler/source/ui/AddSeedDialog.h +++ b/Code/Tools/AssetBundler/source/ui/AddSeedDialog.h @@ -34,7 +34,10 @@ namespace AssetBundler Q_OBJECT public: - explicit AddSeedDialog(QWidget* parent, const AzFramework::PlatformFlags& enabledPlatforms, const AZStd::string& platformSpecificCachePath); + explicit AddSeedDialog( + QWidget* parent, + const AzFramework::PlatformFlags& enabledPlatforms, + const AZStd::string& platformSpecificCachePath); virtual ~AddSeedDialog() {} AZStd::string GetFileName(); diff --git a/Code/Tools/AssetBundler/source/ui/AssetBundlerTabWidget.cpp b/Code/Tools/AssetBundler/source/ui/AssetBundlerTabWidget.cpp index 9dcfd34c35..2d2f40e038 100644 --- a/Code/Tools/AssetBundler/source/ui/AssetBundlerTabWidget.cpp +++ b/Code/Tools/AssetBundler/source/ui/AssetBundlerTabWidget.cpp @@ -73,7 +73,10 @@ namespace AssetBundler SetupContextMenu(); Reload(); - connect(GetFileTableView()->header(), &QHeaderView::sortIndicatorChanged, m_fileTableFilterModel.get(), &AssetBundlerFileTableFilterModel::sort); + connect(GetFileTableView()->header(), + &QHeaderView::sortIndicatorChanged, + m_fileTableFilterModel.get(), + &AssetBundlerFileTableFilterModel::sort); GetFileTableView()->header()->setSortIndicatorShown(true); // Setting this in descending order will ensure the most recent files are at the top GetFileTableView()->header()->setSortIndicator(GetFileTableModel()->GetTimeStampColumnIndex(), Qt::DescendingOrder); @@ -163,9 +166,11 @@ namespace AssetBundler return; } - QString messageBoxText = QString(tr("Are you sure you would like to delete %1? \n\nThis will permanently delete the file.")).arg(QString(selectedFileAbsolutePath.c_str())); + QString messageBoxText = + QString(tr("Are you sure you would like to delete %1? \n\nThis will permanently delete the file.")).arg(QString(selectedFileAbsolutePath.c_str())); - QMessageBox::StandardButton confirmDeleteFileResult = QMessageBox::question(this, QString(tr("Delete %1")).arg(GetFileTypeDisplayName()), messageBoxText); + QMessageBox::StandardButton confirmDeleteFileResult = + QMessageBox::question(this, QString(tr("Delete %1")).arg(GetFileTypeDisplayName()), messageBoxText); if (confirmDeleteFileResult != QMessageBox::StandardButton::Yes) { // User canceled out of the confirmation dialog @@ -206,7 +211,8 @@ namespace AssetBundler defaultFolderPath = m_guiApplicationManager->GetBundlesFolder(); break; default: - AZ_Warning(AssetBundler::AppWindowName, false, "No default folder is defined for AssetBundlingFileType ( %i ).", static_cast(fileType)); + AZ_Warning(AssetBundler::AppWindowName, false, + "No default folder is defined for AssetBundlingFileType ( %i ).", static_cast(fileType)); break; } @@ -224,7 +230,8 @@ namespace AssetBundler void AssetBundlerTabWidget::AddScanPathToAssetBundlerSettings(AssetBundlingFileType fileType, const QString& filePath) { - AZStd::string assetBundlerSettingsFileAbsolutePath = GetAssetBundlerUserSettingsFile(m_guiApplicationManager->GetCurrentProjectFolder().c_str()); + AZStd::string assetBundlerSettingsFileAbsolutePath = + GetAssetBundlerUserSettingsFile(m_guiApplicationManager->GetCurrentProjectFolder().c_str()); QJsonObject assetBundlerSettings = AssetBundler::ReadJson(assetBundlerSettingsFileAbsolutePath); QJsonObject scanPathsSettings = assetBundlerSettings[ScanPathsKey].toObject(); QJsonArray scanPaths = scanPathsSettings[AssetBundlingFileTypes[fileType]].toArray(); @@ -256,7 +263,8 @@ namespace AssetBundler void AssetBundlerTabWidget::RemoveScanPathFromAssetBundlerSettings(AssetBundlingFileType fileType, const QString& filePath) { - AZStd::string assetBundlerSettingsFileAbsolutePath = GetAssetBundlerUserSettingsFile(m_guiApplicationManager->GetCurrentProjectFolder().c_str()); + AZStd::string assetBundlerSettingsFileAbsolutePath = + GetAssetBundlerUserSettingsFile(m_guiApplicationManager->GetCurrentProjectFolder().c_str()); QJsonObject assetBundlerSettings = AssetBundler::ReadJson(assetBundlerSettingsFileAbsolutePath); QJsonObject scanPathsSettings = assetBundlerSettings[ScanPathsKey].toObject(); QJsonArray scanPaths = scanPathsSettings[AssetBundlingFileTypes[fileType]].toArray(); @@ -305,7 +313,10 @@ namespace AssetBundler void AssetBundlerTabWidget::SetupContextMenu() { GetFileTableView()->setContextMenuPolicy(Qt::CustomContextMenu); - connect(GetFileTableView(), &QTreeView::customContextMenuRequested, this, &AssetBundlerTabWidget::OnFileTableContextMenuRequested); + connect(GetFileTableView(), + &QTreeView::customContextMenuRequested, + this, + &AssetBundlerTabWidget::OnFileTableContextMenuRequested); } void AssetBundlerTabWidget::ReadAssetBundlerSettings(const AZStd::string& filePath, AssetBundlingFileType fileType) diff --git a/Code/Tools/AssetBundler/source/ui/AssetBundlerTabWidget.h b/Code/Tools/AssetBundler/source/ui/AssetBundlerTabWidget.h index edef5c0437..17eb171d6d 100644 --- a/Code/Tools/AssetBundler/source/ui/AssetBundlerTabWidget.h +++ b/Code/Tools/AssetBundler/source/ui/AssetBundlerTabWidget.h @@ -78,7 +78,9 @@ namespace AssetBundler virtual void ApplyConfig() = 0; - virtual void FileSelectionChanged(const QItemSelection& /*selected*/ = QItemSelection(), const QItemSelection& /*deselected*/ = QItemSelection()) = 0; + virtual void FileSelectionChanged( + const QItemSelection& /*selected*/ = QItemSelection(), + const QItemSelection& /*deselected*/ = QItemSelection()) = 0; static void InitAssetBundlerSettings(const char* currentProjectFolderPath); diff --git a/Code/Tools/AssetBundler/source/ui/AssetListTabWidget.cpp b/Code/Tools/AssetBundler/source/ui/AssetListTabWidget.cpp index 339dc080fa..2f0351e39e 100644 --- a/Code/Tools/AssetBundler/source/ui/AssetListTabWidget.cpp +++ b/Code/Tools/AssetBundler/source/ui/AssetListTabWidget.cpp @@ -44,14 +44,22 @@ namespace AssetBundler m_ui->mainVerticalLayout->setContentsMargins(10, 10, 10, 10); // File view of all Asset List Files - m_fileTableFilterModel.reset(new AssetBundlerFileTableFilterModel(this, m_fileTableModel->GetFileNameColumnIndex(), m_fileTableModel->GetTimeStampColumnIndex())); + m_fileTableFilterModel.reset(new AssetBundlerFileTableFilterModel( + this, + m_fileTableModel->GetFileNameColumnIndex(), + m_fileTableModel->GetTimeStampColumnIndex())); m_fileTableFilterModel->setSourceModel(m_fileTableModel.data()); m_ui->assetListsTable->setModel(m_fileTableFilterModel.data()); - connect(m_ui->fileFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, - m_fileTableFilterModel.data(), static_cast(&AssetBundlerFileTableFilterModel::FilterChanged)); + connect(m_ui->fileFilteredSearchWidget, + &AzQtComponents::FilteredSearchWidget::TextFilterChanged, + m_fileTableFilterModel.data(), + static_cast(&AssetBundlerFileTableFilterModel::FilterChanged)); - connect(m_ui->assetListsTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &AssetListTabWidget::FileSelectionChanged); + connect(m_ui->assetListsTable->selectionModel(), + &QItemSelectionModel::selectionChanged, + this, + &AssetListTabWidget::FileSelectionChanged); m_ui->fileTableHeaderLayout->setContentsMargins(0, 0, 0, 0); m_ui->fileTableVerticalLayout->setContentsMargins(0, 0, 0, 0); @@ -70,8 +78,10 @@ namespace AssetBundler m_assetListContentsFilterModel->setSourceModel(m_assetListContentsModel.data()); m_ui->assetListContentsTable->setModel(m_assetListContentsFilterModel.data()); - connect(m_ui->assetListContentsFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, - m_assetListContentsFilterModel.data(), static_cast(&AssetBundlerFileTableFilterModel::FilterChanged)); + connect(m_ui->assetListContentsFilteredSearchWidget, + &AzQtComponents::FilteredSearchWidget::TextFilterChanged, + m_assetListContentsFilterModel.data(), + static_cast(&AssetBundlerFileTableFilterModel::FilterChanged)); m_ui->fileContentsHeaderLayout->setContentsMargins(0, 0, 0, 0); @@ -121,13 +131,21 @@ namespace AssetBundler m_ui->fileTableFrame->setFixedWidth(config.fileTableWidth); - m_ui->assetListsTable->header()->resizeSection(AssetListFileTableModel::Column::ColumnFileName, config.assetListFileNameColumnWidth); - m_ui->assetListsTable->header()->resizeSection(AssetListFileTableModel::Column::ColumnPlatform, config.assetListPlatformColumnWidth); + m_ui->assetListsTable->header()->resizeSection( + AssetListFileTableModel::Column::ColumnFileName, + config.assetListFileNameColumnWidth); + m_ui->assetListsTable->header()->resizeSection( + AssetListFileTableModel::Column::ColumnPlatform, + config.assetListPlatformColumnWidth); m_ui->assetListContentsFilteredSearchWidget->setFixedWidth(config.fileTableWidth); - m_ui->assetListContentsTable->header()->resizeSection(AssetListTableModel::Column::ColumnAssetName, config.productAssetNameColumnWidth); - m_ui->assetListContentsTable->header()->resizeSection(AssetListTableModel::Column::ColumnRelativePath, config.productAssetRelativePathColumnWidth); + m_ui->assetListContentsTable->header()->resizeSection( + AssetListTableModel::Column::ColumnAssetName, + config.productAssetNameColumnWidth); + m_ui->assetListContentsTable->header()->resizeSection( + AssetListTableModel::Column::ColumnRelativePath, + config.productAssetRelativePathColumnWidth); } diff --git a/Code/Tools/AssetBundler/source/ui/AssetListTabWidget.h b/Code/Tools/AssetBundler/source/ui/AssetListTabWidget.h index 890f45a0c9..294c32ed38 100644 --- a/Code/Tools/AssetBundler/source/ui/AssetListTabWidget.h +++ b/Code/Tools/AssetBundler/source/ui/AssetListTabWidget.h @@ -65,7 +65,9 @@ namespace AssetBundler AssetBundlerAbstractFileTableModel* GetFileTableModel() override; void SetActiveProjectLabel(const QString& labelText) override; void ApplyConfig() override; - void FileSelectionChanged(const QItemSelection& /*selected*/ = QItemSelection(), const QItemSelection& /*deselected*/ = QItemSelection()) override; + void FileSelectionChanged( + const QItemSelection& /*selected*/ = QItemSelection(), + const QItemSelection& /*deselected*/ = QItemSelection()) override; ////////////////////////////////////////////////////////////////////////// private: diff --git a/Code/Tools/AssetBundler/source/ui/BundleListTabWidget.cpp b/Code/Tools/AssetBundler/source/ui/BundleListTabWidget.cpp index b670fd0ebd..53f0acc0fe 100644 --- a/Code/Tools/AssetBundler/source/ui/BundleListTabWidget.cpp +++ b/Code/Tools/AssetBundler/source/ui/BundleListTabWidget.cpp @@ -34,14 +34,22 @@ namespace AssetBundler m_ui->mainVerticalLayout->setContentsMargins(MarginSize, MarginSize, MarginSize, MarginSize); m_fileTableModel.reset(new BundleFileListModel); - m_fileTableFilterModel.reset(new AssetBundlerFileTableFilterModel(this, m_fileTableModel->GetFileNameColumnIndex(), m_fileTableModel->GetTimeStampColumnIndex())); + m_fileTableFilterModel.reset(new AssetBundlerFileTableFilterModel( + this, + m_fileTableModel->GetFileNameColumnIndex(), + m_fileTableModel->GetTimeStampColumnIndex())); m_fileTableFilterModel->setSourceModel(m_fileTableModel.data()); m_ui->fileTableView->setModel(m_fileTableFilterModel.data()); - connect(m_ui->fileFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, - m_fileTableFilterModel.data(), static_cast(&AssetBundlerFileTableFilterModel::FilterChanged)); + connect(m_ui->fileFilteredSearchWidget, + &AzQtComponents::FilteredSearchWidget::TextFilterChanged, + m_fileTableFilterModel.data(), + static_cast(&AssetBundlerFileTableFilterModel::FilterChanged)); - connect(m_ui->fileTableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &BundleListTabWidget::FileSelectionChanged); + connect(m_ui->fileTableView->selectionModel(), + &QItemSelectionModel::selectionChanged, + this, + &BundleListTabWidget::FileSelectionChanged); m_ui->fileTableView->setIndentation(0); diff --git a/Code/Tools/AssetBundler/source/ui/BundleListTabWidget.h b/Code/Tools/AssetBundler/source/ui/BundleListTabWidget.h index c05e1078fa..83439539c8 100644 --- a/Code/Tools/AssetBundler/source/ui/BundleListTabWidget.h +++ b/Code/Tools/AssetBundler/source/ui/BundleListTabWidget.h @@ -53,7 +53,9 @@ namespace AssetBundler AssetBundlerAbstractFileTableModel* GetFileTableModel() override; void SetActiveProjectLabel(const QString& labelText) override; void ApplyConfig() override; - void FileSelectionChanged(const QItemSelection& /*selected*/ = QItemSelection(), const QItemSelection& /*deselected*/ = QItemSelection()) override; + void FileSelectionChanged( + const QItemSelection& /*selected*/ = QItemSelection(), + const QItemSelection& /*deselected*/ = QItemSelection()) override; private: void ClearDisplayedBundleValues(); diff --git a/Code/Tools/AssetBundler/source/ui/ComparisonDataWidget.cpp b/Code/Tools/AssetBundler/source/ui/ComparisonDataWidget.cpp index b5c2d78030..81055676fb 100644 --- a/Code/Tools/AssetBundler/source/ui/ComparisonDataWidget.cpp +++ b/Code/Tools/AssetBundler/source/ui/ComparisonDataWidget.cpp @@ -42,7 +42,8 @@ namespace AssetBundler if (!IsComparisonDataIndexValid()) { - AZ_Error("AssetBundler", false, "ComparisonData index ( %u ) is out of bounds. ComparisonData cannot be displayed.", m_comparisonDataIndex); + AZ_Error("AssetBundler", false, + "ComparisonData index ( %u ) is out of bounds. ComparisonData cannot be displayed.", m_comparisonDataIndex); return; } @@ -64,14 +65,26 @@ namespace AssetBundler connect(m_ui->nameLineEdit, &QLineEdit::textEdited, this, &ComparisonDataWidget::OnNameLineEditChanged); m_ui->comparisonTypeComboBox->installEventFilter(mouseWheelEventFilter); - connect(m_ui->comparisonTypeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ComparisonDataWidget::OnComparisonTypeComboBoxChanged); + connect(m_ui->comparisonTypeComboBox, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &ComparisonDataWidget::OnComparisonTypeComboBoxChanged); m_ui->firstInputComboBox->installEventFilter(mouseWheelEventFilter); - connect(m_ui->firstInputComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ComparisonDataWidget::OnFirstInputComboBoxChanged); - connect(m_ui->firstInputBrowseButton, &QPushButton::pressed, this, &ComparisonDataWidget::OnFirstInputBrowseButtonPressed); + connect(m_ui->firstInputComboBox, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &ComparisonDataWidget::OnFirstInputComboBoxChanged); + connect(m_ui->firstInputBrowseButton, + &QPushButton::pressed, + this, + &ComparisonDataWidget::OnFirstInputBrowseButtonPressed); m_ui->secondInputComboBox->installEventFilter(mouseWheelEventFilter); - connect(m_ui->secondInputComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ComparisonDataWidget::OnSecondInputComboBoxChanged); + connect(m_ui->secondInputComboBox, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &ComparisonDataWidget::OnSecondInputComboBoxChanged); connect(m_ui->secondInputBrowseButton, &QPushButton::pressed, this, &ComparisonDataWidget::OnSecondInputBrowseButtonPressed); connect(m_ui->filePatternLineEdit, &QLineEdit::textEdited, this, &ComparisonDataWidget::OnFilePatternLineEditChanged); @@ -187,7 +200,8 @@ namespace AssetBundler m_ui->filePatternLineEdit->setText(comparisonData.m_filePattern.c_str()); } - void ComparisonDataWidget::InitComparisonTypeComboBox(const AzToolsFramework::AssetFileInfoListComparison::ComparisonData& comparisonData) + void ComparisonDataWidget::InitComparisonTypeComboBox( + const AzToolsFramework::AssetFileInfoListComparison::ComparisonData& comparisonData) { using namespace AzToolsFramework; @@ -225,7 +239,8 @@ namespace AssetBundler initialSelectionIndex = ComparisonTypeIndex::Complement; break; default: - AZ_Warning("AssetBundler", false, "ComparisonType ( %u ) is not supported in the Asset Bundler", comparisonData.m_comparisonType); + AZ_Warning("AssetBundler", false, + "ComparisonType ( %u ) is not supported in the Asset Bundler", comparisonData.m_comparisonType); } } diff --git a/Code/Tools/AssetBundler/source/ui/EditSeedDialog.cpp b/Code/Tools/AssetBundler/source/ui/EditSeedDialog.cpp index a42e0cd0f0..db243f6bc2 100644 --- a/Code/Tools/AssetBundler/source/ui/EditSeedDialog.cpp +++ b/Code/Tools/AssetBundler/source/ui/EditSeedDialog.cpp @@ -32,7 +32,10 @@ namespace AssetBundler m_ui->platformSelectionWidget->Init(enabledPlatforms); m_ui->platformSelectionWidget->SetSelectedPlatforms(selectedPlatforms, partiallySelectedPlatforms); - connect(m_ui->platformSelectionWidget, &PlatformSelectionWidget::PlatformsSelected, this, &EditSeedDialog::OnPlatformSelectionChanged); + connect(m_ui->platformSelectionWidget, + &PlatformSelectionWidget::PlatformsSelected, + this, + &EditSeedDialog::OnPlatformSelectionChanged); // Set up confirm and cancel buttons connect(m_ui->applyChangesButton, &QPushButton::clicked, this, &QDialog::accept); @@ -49,7 +52,9 @@ namespace AssetBundler return m_ui->platformSelectionWidget->GetPartiallySelectedPlatforms(); } - void EditSeedDialog::OnPlatformSelectionChanged(const AzFramework::PlatformFlags& selectedPlatforms, const AzFramework::PlatformFlags& partiallySelectedPlatforms) + void EditSeedDialog::OnPlatformSelectionChanged( + const AzFramework::PlatformFlags& selectedPlatforms, + const AzFramework::PlatformFlags& partiallySelectedPlatforms) { // Disable the "Apply Changes" button if no platforms are selected bool areAnyPlatformsSelected = selectedPlatforms != AzFramework::PlatformFlags::Platform_NONE || diff --git a/Code/Tools/AssetBundler/source/ui/EditSeedDialog.h b/Code/Tools/AssetBundler/source/ui/EditSeedDialog.h index 751c1eae3d..189212ddbd 100644 --- a/Code/Tools/AssetBundler/source/ui/EditSeedDialog.h +++ b/Code/Tools/AssetBundler/source/ui/EditSeedDialog.h @@ -44,7 +44,9 @@ namespace AssetBundler AzFramework::PlatformFlags GetPartiallySelectedPlatformFlags(); private: - void OnPlatformSelectionChanged(const AzFramework::PlatformFlags& selectedPlatforms, const AzFramework::PlatformFlags& partiallySelectedPlatforms); + void OnPlatformSelectionChanged( + const AzFramework::PlatformFlags& selectedPlatforms, + const AzFramework::PlatformFlags& partiallySelectedPlatforms); QSharedPointer m_ui; }; diff --git a/Code/Tools/AssetBundler/source/ui/GenerateBundlesModal.cpp b/Code/Tools/AssetBundler/source/ui/GenerateBundlesModal.cpp index 3724bc8753..326251af88 100644 --- a/Code/Tools/AssetBundler/source/ui/GenerateBundlesModal.cpp +++ b/Code/Tools/AssetBundler/source/ui/GenerateBundlesModal.cpp @@ -52,26 +52,41 @@ namespace AssetBundler // Bundle Output m_ui->outputBundlePathLineEdit->setReadOnly(true); - connect(m_ui->outputBundlePathBrowseButton, &QPushButton::clicked, this, &GenerateBundlesModal::OnOutputBundleLocationBrowseButtonPressed); + connect(m_ui->outputBundlePathBrowseButton, + &QPushButton::clicked, + this, + &GenerateBundlesModal::OnOutputBundleLocationBrowseButtonPressed); // Bundle Settings files m_ui->bundleSettingsFileLineEdit->setReadOnly(true); m_ui->bundleSettingsFileLineEdit->setText(tr(CustomBundleSettingsText)); - connect(m_ui->bundleSettingsFileBrowseButton, &QPushButton::clicked, this, &GenerateBundlesModal::OnBundleSettingsBrowseButtonPressed); - connect(m_ui->bundleSettingsFileSaveButton, &QPushButton::clicked, this, &GenerateBundlesModal::OnBundleSettingsSaveButtonPressed); + connect(m_ui->bundleSettingsFileBrowseButton, + &QPushButton::clicked, + this, + &GenerateBundlesModal::OnBundleSettingsBrowseButtonPressed); + connect(m_ui->bundleSettingsFileSaveButton, + &QPushButton::clicked, + this, + &GenerateBundlesModal::OnBundleSettingsSaveButtonPressed); // Max Bundle Size m_ui->maxBundleSizeSpinBox->setRange(1, AzToolsFramework::MaxBundleSizeInMB); m_ui->maxBundleSizeSpinBox->setValue(AzToolsFramework::MaxBundleSizeInMB); m_ui->maxBundleSizeSpinBox->setButtonSymbols(QAbstractSpinBox::ButtonSymbols::NoButtons); m_ui->maxBundleSizeSpinBox->setSuffix(" MB"); - connect(m_ui->maxBundleSizeSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &GenerateBundlesModal::OnMaxBundleSizeChanged); + connect(m_ui->maxBundleSizeSpinBox, + QOverload::of(&QSpinBox::valueChanged), + this, + &GenerateBundlesModal::OnMaxBundleSizeChanged); // Bundle Version m_ui->bundleVersionSpinBox->setRange(1, AzFramework::AssetBundleManifest::CurrentBundleVersion); m_ui->bundleVersionSpinBox->setValue(AzFramework::AssetBundleManifest::CurrentBundleVersion); m_ui->bundleVersionSpinBox->setButtonSymbols(QAbstractSpinBox::ButtonSymbols::NoButtons); - connect(m_ui->bundleVersionSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &GenerateBundlesModal::OnBundleVersionChanged); + connect(m_ui->bundleVersionSpinBox, + QOverload::of(&QSpinBox::valueChanged), + this, + &GenerateBundlesModal::OnBundleVersionChanged); // Cancel and Generate Bundles buttons m_ui->generateBundlesButton->setEnabled(false); @@ -197,9 +212,11 @@ namespace AssetBundler if (AZ::IO::FileIOBase::GetInstance()->Exists(bundleSettingsAbsolutePath.c_str())) { - QString messageBoxText = QString(tr("Bundle Settings ( %1 ) already exists on-disk. Saving the current settings will override the existing settings. \n\nDo you wish to continue?")).arg(bundleSettingsAbsolutePath.c_str()); + QString messageBoxText = QString(tr( + "Bundle Settings ( %1 ) already exists on-disk. Saving the current settings will override the existing settings. \n\nDo you wish to continue?")).arg(bundleSettingsAbsolutePath.c_str()); - QMessageBox::StandardButton confirmDeleteFileResult = QMessageBox::question(this, QString(tr("Replace Existing Settings")), messageBoxText); + QMessageBox::StandardButton confirmDeleteFileResult = + QMessageBox::question(this, QString(tr("Replace Existing Settings")), messageBoxText); if (confirmDeleteFileResult != QMessageBox::StandardButton::Yes) { // User canceled out of the operation @@ -237,9 +254,11 @@ namespace AssetBundler if (AZ::IO::FileIOBase::GetInstance()->Exists(m_bundleSettings.m_bundleFilePath.c_str())) { - QString messageBoxText = QString(tr("Asset Bundle ( %1 ) already exists on-disk. Generating a new Bundle will override the existing Bundle. \n\nDo you wish to permanently delete the existing Bundle?")).arg(m_bundleSettings.m_bundleFilePath.c_str()); + QString messageBoxText = QString(tr( + "Asset Bundle ( %1 ) already exists on-disk. Generating a new Bundle will override the existing Bundle. \n\nDo you wish to permanently delete the existing Bundle?")).arg(m_bundleSettings.m_bundleFilePath.c_str()); - QMessageBox::StandardButton confirmDeleteFileResult = QMessageBox::question(this, QString(tr("Replace Existing Bundle")), messageBoxText); + QMessageBox::StandardButton confirmDeleteFileResult = + QMessageBox::question(this, QString(tr("Replace Existing Bundle")), messageBoxText); if (confirmDeleteFileResult != QMessageBox::StandardButton::Yes) { // User canceled out of the operation @@ -259,10 +278,14 @@ namespace AssetBundler if (result) { - m_assetListTabWidget->AddScanPathToAssetBundlerSettings(AssetBundlingFileType::BundleFileType, m_bundleSettings.m_bundleFilePath); + m_assetListTabWidget->AddScanPathToAssetBundlerSettings( + AssetBundlingFileType::BundleFileType, + m_bundleSettings.m_bundleFilePath); // The watched files list was updated after the files were created, so we need to force-reload them - m_assetListTabWidget->GetGUIApplicationManager()->UpdateFiles(AssetBundlingFileType::BundleFileType, { m_bundleSettings.m_bundleFilePath }); + m_assetListTabWidget->GetGUIApplicationManager()->UpdateFiles( + AssetBundlingFileType::BundleFileType, + { m_bundleSettings.m_bundleFilePath }); } AZStd::vector generatedFilePaths = { m_bundleSettings.m_bundleFilePath }; diff --git a/Code/Tools/AssetBundler/source/ui/MainWindow.cpp b/Code/Tools/AssetBundler/source/ui/MainWindow.cpp index c0e30c01f2..53b1c07943 100644 --- a/Code/Tools/AssetBundler/source/ui/MainWindow.cpp +++ b/Code/Tools/AssetBundler/source/ui/MainWindow.cpp @@ -63,7 +63,10 @@ namespace AssetBundler // Set up Tabs AssetBundlerTabWidget::InitAssetBundlerSettings(m_guiApplicationManager->GetCurrentProjectFolder().c_str()); - m_seedListTab.reset(new SeedTabWidget(this, m_guiApplicationManager, QString(m_guiApplicationManager->GetAssetBundlingFolder().c_str()))); + m_seedListTab.reset(new SeedTabWidget( + this, + m_guiApplicationManager, + QString(m_guiApplicationManager->GetAssetBundlingFolder().c_str()))); m_ui->tabWidget->addTab(m_seedListTab.data(), m_seedListTab->GetTabTitle()); m_assetListTab.reset(new AssetListTabWidget(this, m_guiApplicationManager)); diff --git a/Code/Tools/AssetBundler/source/ui/NewFileDialog.cpp b/Code/Tools/AssetBundler/source/ui/NewFileDialog.cpp index 170da4a659..00a63d193b 100644 --- a/Code/Tools/AssetBundler/source/ui/NewFileDialog.cpp +++ b/Code/Tools/AssetBundler/source/ui/NewFileDialog.cpp @@ -51,7 +51,8 @@ namespace AssetBundler m_newFileDialog.setNameFilter(fileNameFilter); m_newFileDialog.setViewMode(QFileDialog::Detail); m_newFileDialog.setDirectory(m_startingPath); - // We are not creating a new file when Qt thinks we are, so we need to block signals or else the file watcher will be triggered too soon + // We are not creating a new file when Qt thinks we are, so we need to block signals or else the file watcher will be + // triggered too soon m_newFileDialog.blockSignals(true); // Set up Platform selection @@ -61,7 +62,10 @@ namespace AssetBundler disabledPatformMessageOverride = tr("This platform is not valid for all input Asset Lists."); } m_ui->platformSelectionWidget->Init(enabledPlatforms, disabledPatformMessageOverride); - connect(m_ui->platformSelectionWidget, &PlatformSelectionWidget::PlatformsSelected, this, &NewFileDialog::OnPlatformSelectionChanged); + connect(m_ui->platformSelectionWidget, + &PlatformSelectionWidget::PlatformsSelected, + this, + &NewFileDialog::OnPlatformSelectionChanged); // Set up Cancel and Create New File buttons m_ui->createFileButton->setEnabled(false); @@ -112,7 +116,8 @@ namespace AssetBundler { // Check to see if any of the selected platform-specific files already exist on-disk QString overwriteExistingFilesList; - AZStd::fixed_vector selectedPlatformNames = AzFramework::PlatformHelper::GetPlatforms(GetPlatformFlags()); + AZStd::fixed_vector selectedPlatformNames = + AzFramework::PlatformHelper::GetPlatforms(GetPlatformFlags()); for (const AZStd::string& platformName : selectedPlatformNames) { FilePath platformSpecificFilePath(GetAbsoluteFilePath(), platformName); @@ -126,9 +131,11 @@ namespace AssetBundler // Ask the user if they are sure they want to overwrite existing files if (!overwriteExistingFilesList.isEmpty()) { - QString messageBoxText = QString(tr("The following files already exist on-disk. Generating new files will overwrite the existing ones.\n\n%1\n\nDo you wish to permanently delete the existing files?")).arg(overwriteExistingFilesList); + QString messageBoxText = QString(tr( + "The following files already exist on-disk. Generating new files will overwrite the existing ones.\n\n%1\n\nDo you wish to permanently delete the existing files?")).arg(overwriteExistingFilesList); - QMessageBox::StandardButton confirmDeleteFileResult = QMessageBox::question(this, QString(tr("Replace Existing Files")), messageBoxText); + QMessageBox::StandardButton confirmDeleteFileResult = + QMessageBox::question(this, QString(tr("Replace Existing Files")), messageBoxText); if (confirmDeleteFileResult != QMessageBox::StandardButton::Yes) { // User canceled out of the operation @@ -139,7 +146,11 @@ namespace AssetBundler emit QDialog::accept(); } - AZStd::string NewFileDialog::OSNewFileDialog(QWidget* parent, const char* fileExtension, const char* fileTypeDisplayName, const AZStd::string& startingDirectory) + AZStd::string NewFileDialog::OSNewFileDialog( + QWidget* parent, + const char* fileExtension, + const char* fileTypeDisplayName, + const AZStd::string& startingDirectory) { QFileDialog filePathDialog(parent); filePathDialog.setFileMode(QFileDialog::AnyFile); @@ -160,13 +171,17 @@ namespace AssetBundler AZStd::string absoluteFilePath(filePathDialog.selectedFiles()[0].toUtf8().data()); if (!AzFramework::StringFunc::Path::HasExtension(absoluteFilePath.c_str())) { - absoluteFilePath = AZStd::string::format("%s%c%s", absoluteFilePath.c_str(), AZ_FILESYSTEM_EXTENSION_SEPARATOR, fileExtension); + absoluteFilePath = + AZStd::string::format("%s%c%s", absoluteFilePath.c_str(), AZ_FILESYSTEM_EXTENSION_SEPARATOR, fileExtension); } return absoluteFilePath; } - int NewFileDialog::FileGenerationResultMessageBox(QWidget* parent, const AZStd::vector& generatedFiles, bool generatedWithErrors) + int NewFileDialog::FileGenerationResultMessageBox( + QWidget* parent, + const AZStd::vector& generatedFiles, + bool generatedWithErrors) { QMessageBox messageBox(parent); messageBox.setStandardButtons(QMessageBox::Ok); diff --git a/Code/Tools/AssetBundler/source/ui/NewFileDialog.h b/Code/Tools/AssetBundler/source/ui/NewFileDialog.h index 5974d3da6a..37cb81a3d3 100644 --- a/Code/Tools/AssetBundler/source/ui/NewFileDialog.h +++ b/Code/Tools/AssetBundler/source/ui/NewFileDialog.h @@ -51,9 +51,16 @@ namespace AssetBundler //! A standard OS-specific New File Dialog, but blocks all Qt signals from the dialog and does NOT create a new file. //! Use in place of the static QFileDialog functions to avoid unexpected file watcher updates. //! Returns the absolute path of the file the user either selected or attempted to create, or an empty string if the user canceled out of the dialog. - static AZStd::string OSNewFileDialog(QWidget* parent, const char* fileExtension, const char* fileTypeDisplayName, const AZStd::string& startingDirectory); + static AZStd::string OSNewFileDialog( + QWidget* parent, + const char* fileExtension, + const char* fileTypeDisplayName, + const AZStd::string& startingDirectory); - static int FileGenerationResultMessageBox(QWidget* parent, const AZStd::vector& generatedFiles, bool generatedWithErrors); + static int FileGenerationResultMessageBox( + QWidget* parent, + const AZStd::vector& generatedFiles, + bool generatedWithErrors); private: void OnBrowseButtonPressed(); diff --git a/Code/Tools/AssetBundler/source/ui/PlatformSelectionWidget.cpp b/Code/Tools/AssetBundler/source/ui/PlatformSelectionWidget.cpp index 80b4590c3c..ae203077fe 100644 --- a/Code/Tools/AssetBundler/source/ui/PlatformSelectionWidget.cpp +++ b/Code/Tools/AssetBundler/source/ui/PlatformSelectionWidget.cpp @@ -66,7 +66,9 @@ namespace AssetBundler } } - void PlatformSelectionWidget::SetSelectedPlatforms(const AzFramework::PlatformFlags& selectedPlatforms, const AzFramework::PlatformFlags& partiallySelectedPlatforms) + void PlatformSelectionWidget::SetSelectedPlatforms( + const AzFramework::PlatformFlags& selectedPlatforms, + const AzFramework::PlatformFlags& partiallySelectedPlatforms) { m_selectedPlatforms = AzFramework::PlatformFlags::Platform_NONE; m_partiallySelectedPlatforms = AzFramework::PlatformFlags::Platform_NONE; diff --git a/Code/Tools/AssetBundler/source/ui/PlatformSelectionWidget.h b/Code/Tools/AssetBundler/source/ui/PlatformSelectionWidget.h index 223044478f..10e47d0385 100644 --- a/Code/Tools/AssetBundler/source/ui/PlatformSelectionWidget.h +++ b/Code/Tools/AssetBundler/source/ui/PlatformSelectionWidget.h @@ -39,7 +39,9 @@ namespace AssetBundler void Init(const AzFramework::PlatformFlags& enabledPlatforms, const QString& disabledPatformMessageOverride = ""); - void SetSelectedPlatforms(const AzFramework::PlatformFlags& selectedPlatforms, const AzFramework::PlatformFlags& partiallySelectedPlatforms); + void SetSelectedPlatforms( + const AzFramework::PlatformFlags& selectedPlatforms, + const AzFramework::PlatformFlags& partiallySelectedPlatforms); AzFramework::PlatformFlags GetSelectedPlatforms(); AzFramework::PlatformFlags GetPartiallySelectedPlatforms(); diff --git a/Code/Tools/AssetBundler/source/ui/RulesTabWidget.cpp b/Code/Tools/AssetBundler/source/ui/RulesTabWidget.cpp index 56447f4a57..81eb5f3b61 100644 --- a/Code/Tools/AssetBundler/source/ui/RulesTabWidget.cpp +++ b/Code/Tools/AssetBundler/source/ui/RulesTabWidget.cpp @@ -45,14 +45,22 @@ namespace AssetBundler m_ui->fileTableView->setModel(m_fileTableModel.data()); // Table View of all Rules files - m_fileTableFilterModel.reset(new AssetBundlerFileTableFilterModel(this, m_fileTableModel->GetFileNameColumnIndex(), m_fileTableModel->GetTimeStampColumnIndex())); + m_fileTableFilterModel.reset(new AssetBundlerFileTableFilterModel( + this, + m_fileTableModel->GetFileNameColumnIndex(), + m_fileTableModel->GetTimeStampColumnIndex())); m_fileTableFilterModel->setSourceModel(m_fileTableModel.data()); m_ui->fileTableView->setModel(m_fileTableFilterModel.data()); - connect(m_ui->fileFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, - m_fileTableFilterModel.data(), static_cast(&AssetBundlerFileTableFilterModel::FilterChanged)); + connect(m_ui->fileFilteredSearchWidget, + &AzQtComponents::FilteredSearchWidget::TextFilterChanged, + m_fileTableFilterModel.data(), + static_cast(&AssetBundlerFileTableFilterModel::FilterChanged)); - connect(m_ui->fileTableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &RulesTabWidget::FileSelectionChanged); + connect(m_ui->fileTableView->selectionModel(), + &QItemSelectionModel::selectionChanged, + this, + &RulesTabWidget::FileSelectionChanged); m_ui->fileTableView->setIndentation(0); @@ -76,7 +84,10 @@ namespace AssetBundler void RulesTabWidget::Reload() { - m_fileTableModel->Reload(AzToolsFramework::AssetFileInfoListComparison::GetComparisonRulesFileExtension(), m_watchedFolders, m_watchedFiles); + m_fileTableModel->Reload( + AzToolsFramework::AssetFileInfoListComparison::GetComparisonRulesFileExtension(), + m_watchedFolders, + m_watchedFiles); FileSelectionChanged(); } @@ -223,7 +234,8 @@ namespace AssetBundler AZStd::vector outputFilePaths; bool hasFileGenerationErrors = false; - AZStd::fixed_vector selectedPlatformNames = AzFramework::PlatformHelper::GetPlatforms(runRuleDialog.GetPlatformFlags()); + AZStd::fixed_vector selectedPlatformNames = + AzFramework::PlatformHelper::GetPlatforms(runRuleDialog.GetPlatformFlags()); for (const AZStd::string& platformName : selectedPlatformNames) { // We do not want to modify the original Rules file, as we do not save Asset List file paths to disk @@ -238,7 +250,8 @@ namespace AssetBundler { if (comparisonStep.m_cachedFirstInputPath.empty()) { - AZ_Error("AssetBundler", false, "Unable to run Rule: Comparison Step #%u has no specified first input.", comparisonStepIndex); + AZ_Error("AssetBundler", false, + "Unable to run Rule: Comparison Step #%u has no specified first input.", comparisonStepIndex); return; } @@ -251,7 +264,8 @@ namespace AssetBundler { if (comparisonStep.m_cachedSecondInputPath.empty()) { - AZ_Error("AssetBundler", false, "Unable to run Rule: Comparison Step #%u has no specified second input.", comparisonStepIndex); + AZ_Error("AssetBundler", false, + "Unable to run Rule: Comparison Step #%u has no specified second input.", comparisonStepIndex); return; } @@ -313,17 +327,28 @@ namespace AssetBundler } } - void RulesTabWidget::CreateComparisonDataCard(AZStd::shared_ptr comparisonList, size_t comparisonDataIndex) + void RulesTabWidget::CreateComparisonDataCard( + AZStd::shared_ptr comparisonList, + size_t comparisonDataIndex) { - ComparisonDataCard* comparisonDataCard = new ComparisonDataCard(comparisonList, comparisonDataIndex, m_guiApplicationManager->GetAssetListsFolder()); + ComparisonDataCard* comparisonDataCard = new ComparisonDataCard( + comparisonList, + comparisonDataIndex, + m_guiApplicationManager->GetAssetListsFolder()); comparisonDataCard->setTitle(tr("Step %1").arg(static_cast(comparisonDataIndex) + 1)); m_ui->comparisonDataListLayout->addWidget(comparisonDataCard); m_comparisonDataCardList.push_back(comparisonDataCard); ComparisonDataWidget* comparisonDataWidget = comparisonDataCard->GetComparisonDataWidget(); - connect(comparisonDataCard, &ComparisonDataCard::comparisonDataCardContextMenuRequested, this, &RulesTabWidget::OnComparisonDataCardContextMenuRequested); + connect(comparisonDataCard, + &ComparisonDataCard::comparisonDataCardContextMenuRequested, + this, + &RulesTabWidget::OnComparisonDataCardContextMenuRequested); connect(comparisonDataWidget, &ComparisonDataWidget::comparisonDataChanged, this, &RulesTabWidget::MarkFileChanged); - connect(comparisonDataWidget, &ComparisonDataWidget::comparisonDataTokenNameChanged, this, &RulesTabWidget::OnAnyTokenNameChanged); + connect(comparisonDataWidget, + &ComparisonDataWidget::comparisonDataTokenNameChanged, + this, + &RulesTabWidget::OnAnyTokenNameChanged); comparisonDataCard->show(); } diff --git a/Code/Tools/AssetBundler/source/ui/RulesTabWidget.h b/Code/Tools/AssetBundler/source/ui/RulesTabWidget.h index 7dedce3efe..a48bd47617 100644 --- a/Code/Tools/AssetBundler/source/ui/RulesTabWidget.h +++ b/Code/Tools/AssetBundler/source/ui/RulesTabWidget.h @@ -81,7 +81,9 @@ namespace AssetBundler void ApplyConfig() override; - void FileSelectionChanged(const QItemSelection& /*selected*/ = QItemSelection(), const QItemSelection& /*deselected*/ = QItemSelection()) override; + void FileSelectionChanged( + const QItemSelection& /*selected*/ = QItemSelection(), + const QItemSelection& /*deselected*/ = QItemSelection()) override; private: void OnNewFileButtonPressed(); @@ -94,7 +96,9 @@ namespace AssetBundler void PopulateComparisonDataCardList(); - void CreateComparisonDataCard(AZStd::shared_ptr comparisonList, size_t comparisonDataIndex); + void CreateComparisonDataCard( + AZStd::shared_ptr comparisonList, + size_t comparisonDataIndex); void RemoveAllComparisonDataCards(); diff --git a/Code/Tools/AssetBundler/source/ui/SeedTabWidget.cpp b/Code/Tools/AssetBundler/source/ui/SeedTabWidget.cpp index 60b4104c5a..26e700a170 100644 --- a/Code/Tools/AssetBundler/source/ui/SeedTabWidget.cpp +++ b/Code/Tools/AssetBundler/source/ui/SeedTabWidget.cpp @@ -56,14 +56,22 @@ namespace AssetBundler AZ::Debug::TraceMessageBus::Handler::BusConnect(); // File view of all Seed List Files - m_fileTableFilterModel.reset(new AssetBundlerFileTableFilterModel(this, m_fileTableModel->GetFileNameColumnIndex(), m_fileTableModel->GetTimeStampColumnIndex())); + m_fileTableFilterModel.reset(new AssetBundlerFileTableFilterModel( + this, + m_fileTableModel->GetFileNameColumnIndex(), + m_fileTableModel->GetTimeStampColumnIndex())); m_fileTableFilterModel->setSourceModel(m_fileTableModel.data()); m_ui->fileTableView->setModel(m_fileTableFilterModel.data()); - connect(m_ui->fileFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, - m_fileTableFilterModel.data(), static_cast(&AssetBundlerFileTableFilterModel::FilterChanged)); + connect(m_ui->fileFilteredSearchWidget, + &AzQtComponents::FilteredSearchWidget::TextFilterChanged, + m_fileTableFilterModel.data(), + static_cast(&AssetBundlerFileTableFilterModel::FilterChanged)); - connect(m_ui->fileTableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &SeedTabWidget::FileSelectionChanged); + connect(m_ui->fileTableView->selectionModel(), + &QItemSelectionModel::selectionChanged, + this, + &SeedTabWidget::FileSelectionChanged); m_ui->fileTableView->setIndentation(CheckBoxTableIndentationSize); @@ -82,11 +90,16 @@ namespace AssetBundler m_seedListContentsFilterModel->setSourceModel(m_seedListContentsModel.data()); m_ui->seedFileContentsTable->setModel(m_seedListContentsFilterModel.data()); - connect(m_ui->seedListContentsFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, - m_seedListContentsFilterModel.data(), static_cast(&AssetBundlerFileTableFilterModel::FilterChanged)); + connect(m_ui->seedListContentsFilteredSearchWidget, + &AzQtComponents::FilteredSearchWidget::TextFilterChanged, + m_seedListContentsFilterModel.data(), + static_cast(&AssetBundlerFileTableFilterModel::FilterChanged)); m_ui->seedFileContentsTable->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_ui->seedFileContentsTable, &QTreeView::customContextMenuRequested, this, &SeedTabWidget::OnSeedListContentsTableContextMenuRequested); + connect(m_ui->seedFileContentsTable, + &QTreeView::customContextMenuRequested, + this, + &SeedTabWidget::OnSeedListContentsTableContextMenuRequested); m_ui->seedFileContentsTable->setIndentation(0); @@ -112,7 +125,11 @@ namespace AssetBundler void SeedTabWidget::Reload() { // Reload all the seed list files - m_fileTableModel->Reload(AzToolsFramework::AssetSeedManager::GetSeedFileExtension(), m_watchedFolders, m_watchedFiles, m_filePathToGemNameMap); + m_fileTableModel->Reload( + AzToolsFramework::AssetSeedManager::GetSeedFileExtension(), + m_watchedFolders, + m_watchedFiles, + m_filePathToGemNameMap); // Update the selected row FileSelectionChanged(); @@ -138,12 +155,18 @@ namespace AssetBundler m_watchedFolders.insert(m_guiApplicationManager->GetSeedListsFolder().c_str()); // Get the list of default Seed List files - m_filePathToGemNameMap = AssetBundler::GetDefaultSeedListFiles(AZStd::string_view{ AZ::Utils::GetEnginePath() }, m_guiApplicationManager->GetCurrentProjectName(), + m_filePathToGemNameMap = AssetBundler::GetDefaultSeedListFiles( + AZStd::string_view{ AZ::Utils::GetEnginePath() }, + m_guiApplicationManager->GetCurrentProjectName(), m_guiApplicationManager->GetGemInfoList(), m_guiApplicationManager->GetEnabledPlatforms()); // Get the list of default Seeds that are not stored in a Seed List file on-disk - AZStd::vector defaultSeeds = GetDefaultSeeds(AZ::Utils::GetProjectPath(), m_guiApplicationManager->GetCurrentProjectName()); - m_fileTableModel->AddDefaultSeedsToInMemoryList(defaultSeeds, m_guiApplicationManager->GetCurrentProjectName().c_str(), m_guiApplicationManager->GetEnabledPlatforms()); + AZStd::vector defaultSeeds = + GetDefaultSeeds(AZ::Utils::GetProjectPath(), m_guiApplicationManager->GetCurrentProjectName()); + m_fileTableModel->AddDefaultSeedsToInMemoryList( + defaultSeeds, + m_guiApplicationManager->GetCurrentProjectName().c_str(), + m_guiApplicationManager->GetEnabledPlatforms()); // Set the new watched filess for the model m_watchedFiles.clear(); @@ -185,7 +208,9 @@ namespace AssetBundler m_ui->fileTableView->header()->resizeSection(SeedListFileTableModel::Column::ColumnCheckBox, config.checkBoxColumnWidth); m_ui->fileTableView->header()->resizeSection(SeedListFileTableModel::Column::ColumnProject, config.projectNameColumnWidth); - m_ui->seedFileContentsTable->header()->resizeSection(SeedListTableModel::Column::ColumnRelativePath, config.seedListContentsNameColumnWidth); + m_ui->seedFileContentsTable->header()->resizeSection( + SeedListTableModel::Column::ColumnRelativePath, + config.seedListContentsNameColumnWidth); } void SeedTabWidget::UncheckSelectDefaultSeedListsCheckBox() @@ -198,15 +223,25 @@ namespace AssetBundler m_ui->generateAssetListsButton->setEnabled(isEnabled); } - bool SeedTabWidget::OnPreError(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) + bool SeedTabWidget::OnPreError( + const char* /*window*/, + const char* /*fileName*/, + int /*line*/, + const char* /*func*/, + const char* /*message*/) { - m_hasWarnings = true; + m_hasWarningsOrErrors = true; return false; } - bool SeedTabWidget::OnPreWarning(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) + bool SeedTabWidget::OnPreWarning( + const char* /*window*/, + const char* /*fileName*/, + int /*line*/, + const char* /*func*/, + const char* /*message*/) { - m_hasWarnings = true; + m_hasWarningsOrErrors = true; return false; } @@ -275,11 +310,13 @@ namespace AssetBundler return; } - m_hasWarnings = false; - auto createdFiles = m_fileTableModel->GenerateAssetLists(m_generateAssetListsDialog->GetAbsoluteFilePath(), m_generateAssetListsDialog->GetPlatformFlags()); + m_hasWarningsOrErrors = false; + auto createdFiles = m_fileTableModel->GenerateAssetLists( + m_generateAssetListsDialog->GetAbsoluteFilePath(), + m_generateAssetListsDialog->GetPlatformFlags()); // Warnings will not prevent the generation of Asset List files, we must track them separately - NewFileDialog::FileGenerationResultMessageBox(this, createdFiles, m_hasWarnings); + NewFileDialog::FileGenerationResultMessageBox(this, createdFiles, m_hasWarningsOrErrors); if (createdFiles.empty()) { @@ -306,7 +343,8 @@ namespace AssetBundler } // Get the current platforms of the selected Seed so we can display them as already checked - QModelIndex currentSeedIndex = m_seedListContentsFilterModel->mapToSource(m_ui->seedFileContentsTable->selectionModel()->currentIndex()); + QModelIndex currentSeedIndex = + m_seedListContentsFilterModel->mapToSource(m_ui->seedFileContentsTable->selectionModel()->currentIndex()); auto getPlatformOutcome = m_seedListContentsModel->GetSeedPlatforms(currentSeedIndex); if (!getPlatformOutcome.IsSuccess()) { @@ -375,7 +413,8 @@ namespace AssetBundler AzFramework::PlatformFlags checkedPlatforms = m_editSeedDialog->GetPlatformFlags(); AzFramework::PlatformFlags partiallyCheckedPlatforms = m_editSeedDialog->GetPartiallySelectedPlatformFlags(); // If the platform is partially checked, we want to keep its original status when saving the changes - AzFramework::PlatformFlags platformFlags = indexToPlatformFlagsMap[currentSeedIndex] & partiallyCheckedPlatforms | checkedPlatforms; + AzFramework::PlatformFlags platformFlags = + indexToPlatformFlagsMap[currentSeedIndex] & partiallyCheckedPlatforms | checkedPlatforms; m_fileTableModel->SetSeedPlatforms(m_selectedFileTableIndex, currentSeedIndex, platformFlags); } @@ -391,8 +430,10 @@ namespace AssetBundler // Get path to the platform-specific cache folder of one of the enabled platforms AzFramework::PlatformFlags enabledPlatforms = m_guiApplicationManager->GetEnabledPlatforms(); - AZStd::fixed_vector enabledPlatformIndices = AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(enabledPlatforms); - AZStd::string platformSpecificCachePath = AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(enabledPlatformIndices[0]); + AZStd::fixed_vector enabledPlatformIndices = + AzFramework::PlatformHelper::GetPlatformIndicesInterpreted(enabledPlatforms); + AZStd::string platformSpecificCachePath = + AzToolsFramework::PlatformAddressedAssetCatalog::GetCatalogRegistryPathForPlatform(enabledPlatformIndices[0]); // Create and display the Add Seed Dialog m_addSeedDialog.reset(new AddSeedDialog(this, enabledPlatforms, platformSpecificCachePath)); @@ -416,7 +457,8 @@ namespace AssetBundler } // Set the data in the model - QModelIndex currentSeedIndex = m_seedListContentsFilterModel->mapToSource(m_ui->seedFileContentsTable->selectionModel()->currentIndex()); + QModelIndex currentSeedIndex = + m_seedListContentsFilterModel->mapToSource(m_ui->seedFileContentsTable->selectionModel()->currentIndex()); m_fileTableModel->RemoveSeed(m_selectedFileTableIndex, currentSeedIndex); } diff --git a/Code/Tools/AssetBundler/source/ui/SeedTabWidget.h b/Code/Tools/AssetBundler/source/ui/SeedTabWidget.h index c1383cdd08..784b8edd80 100644 --- a/Code/Tools/AssetBundler/source/ui/SeedTabWidget.h +++ b/Code/Tools/AssetBundler/source/ui/SeedTabWidget.h @@ -82,7 +82,9 @@ namespace AssetBundler void ApplyConfig() override; - void FileSelectionChanged(const QItemSelection& /*selected*/ = QItemSelection(), const QItemSelection& /*deselected*/ = QItemSelection()) override; + void FileSelectionChanged( + const QItemSelection& /*selected*/ = QItemSelection(), + const QItemSelection& /*deselected*/ = QItemSelection()) override; void UncheckSelectDefaultSeedListsCheckBox(); @@ -124,6 +126,6 @@ namespace AssetBundler QSharedPointer m_editSeedDialog; QSharedPointer m_addSeedDialog; - bool m_hasWarnings = false; + bool m_hasWarningsOrErrors = false; }; } // namespace AssetBundler diff --git a/Code/Tools/AssetBundler/source/utils/GUIApplicationManager.cpp b/Code/Tools/AssetBundler/source/utils/GUIApplicationManager.cpp index d278b9bd2c..e4dd5edba7 100644 --- a/Code/Tools/AssetBundler/source/utils/GUIApplicationManager.cpp +++ b/Code/Tools/AssetBundler/source/utils/GUIApplicationManager.cpp @@ -83,7 +83,8 @@ namespace AssetBundler ConfigHelpers::read(settings, QStringLiteral("AssetListFileNameColumnWidth"), config.assetListFileNameColumnWidth); ConfigHelpers::read(settings, QStringLiteral("AssetListPlatformColumnWidth"), config.assetListPlatformColumnWidth); ConfigHelpers::read(settings, QStringLiteral("ProductAssetNameColumnWidth"), config.productAssetNameColumnWidth); - ConfigHelpers::read(settings, QStringLiteral("ProductAssetRelativePathColumnWidth"), config.productAssetRelativePathColumnWidth); + ConfigHelpers::read( + settings, QStringLiteral("ProductAssetRelativePathColumnWidth"), config.productAssetRelativePathColumnWidth); } return config; @@ -159,8 +160,7 @@ namespace AssetBundler m_platformCatalogManager = AZStd::make_unique(); // Define some application-level settings - QApplication::setOrganizationName("Amazon"); - QApplication::setOrganizationDomain("amazon.com"); + QApplication::setOrganizationName("O3DE"); QApplication::setApplicationName("Asset Bundler"); QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates)); @@ -194,7 +194,12 @@ namespace AssetBundler engineRoot); AzQtComponents::StyleManager::setStyleSheet(m_mainWindow.data(), QStringLiteral("style:AssetBundler.qss")); - AzQtComponents::ConfigHelpers::loadConfig(&m_fileWatcher, &m_config, QStringLiteral("style:AssetBundlerConfig.ini"), this, std::bind(&GUIApplicationManager::ApplyConfig, this)); + AzQtComponents::ConfigHelpers::loadConfig( + &m_fileWatcher, + &m_config, + QStringLiteral("style:AssetBundlerConfig.ini"), + this, + std::bind(&GUIApplicationManager::ApplyConfig, this)); ApplyConfig(); qApp->setWindowIcon(QIcon("style:AssetBundler-Icon-256x256@x2.ico")); @@ -238,7 +243,12 @@ namespace AssetBundler m_fileWatcher.removePaths(paths.values()); } - bool GUIApplicationManager::OnPreError(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message) + bool GUIApplicationManager::OnPreError( + const char* /*window*/, + const char* /*fileName*/, + int /*line*/, + const char* /*func*/, + const char* message) { // We want to display errors during initialization, then let the MainWindow handle errors during runtime if (m_isInitializing) @@ -258,7 +268,12 @@ namespace AssetBundler return false; } - bool GUIApplicationManager::OnPreWarning(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* /*message*/) + bool GUIApplicationManager::OnPreWarning( + const char* /*window*/, + const char* /*fileName*/, + int /*line*/, + const char* /*func*/, + const char* /*message*/) { // Don't handle warnings, let the MainWindow print them return false; diff --git a/Code/Tools/AssetBundler/source/utils/utils.cpp b/Code/Tools/AssetBundler/source/utils/utils.cpp index 39bc1a7e13..b91dcde9e2 100644 --- a/Code/Tools/AssetBundler/source/utils/utils.cpp +++ b/Code/Tools/AssetBundler/source/utils/utils.cpp @@ -155,8 +155,10 @@ namespace AssetBundler if (fileIO->Exists(platformDirectory.c_str())) { bool recurse = true; - AZ::Outcome, AZStd::string> result = AzFramework::FileFunc::FindFileList(platformDirectory.String(), - AZStd::string::format("*.%s", AzToolsFramework::AssetSeedManager::GetSeedFileExtension()).c_str(), recurse); + AZ::Outcome, AZStd::string> result = AzFramework::FileFunc::FindFileList( + platformDirectory.String(), + AZStd::string::format("*.%s", AzToolsFramework::AssetSeedManager::GetSeedFileExtension()).c_str(), + recurse); if (result.IsSuccess()) { @@ -233,8 +235,11 @@ namespace AssetBundler return platformFlags; } - AZStd::unordered_map GetDefaultSeedListFiles(AZStd::string_view enginePath, AZStd::string_view projectPath, - const AZStd::vector& gemInfoList, AzFramework::PlatformFlags platformFlag) + AZStd::unordered_map GetDefaultSeedListFiles( + AZStd::string_view enginePath, + AZStd::string_view projectPath, + const AZStd::vector& gemInfoList, + AzFramework::PlatformFlags platformFlag) { AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); AZ_Assert(fileIO, "AZ::IO::FileIOBase must be ready for use.\n"); @@ -299,7 +304,9 @@ namespace AssetBundler return relativeProductPath; } - AZStd::unordered_map GetGemSeedListFilePathToGemNameMap(const AZStd::vector& gemInfoList, AzFramework::PlatformFlags platformFlags) + AZStd::unordered_map GetGemSeedListFilePathToGemNameMap( + const AZStd::vector& gemInfoList, + AzFramework::PlatformFlags platformFlags) { AZStd::unordered_map filePathToGemNameMap; for (const AzFramework::GemInfo& gemInfo : gemInfoList) @@ -325,7 +332,11 @@ namespace AssetBundler return filePathToGemNameMap; } - bool IsGemSeedFilePathValid(AZStd::string_view engineRoot, AZStd::string seedAbsoluteFilePath, const AZStd::vector& gemInfoList, AzFramework::PlatformFlags platformFlags) + bool IsGemSeedFilePathValid( + AZStd::string_view engineRoot, + AZStd::string seedAbsoluteFilePath, + const AZStd::vector& gemInfoList, + AzFramework::PlatformFlags platformFlags) { AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); AZ_Assert(fileIO, "AZ::IO::FileIOBase must be ready for use.\n"); @@ -369,7 +380,10 @@ namespace AssetBundler return false; } - AzFramework::PlatformFlags GetEnabledPlatformFlags(AZStd::string_view engineRoot, AZStd::string_view assetRoot, AZStd::string_view projectPath) + AzFramework::PlatformFlags GetEnabledPlatformFlags( + AZStd::string_view engineRoot, + AZStd::string_view assetRoot, + AZStd::string_view projectPath) { auto settingsRegistry = AZ::SettingsRegistry::Get(); if (settingsRegistry == nullptr) @@ -391,7 +405,8 @@ namespace AssetBundler } else { - AZ_Warning(AssetBundler::AppWindowName, false, "Platform Helper is not aware of the platform (%s).\n ", enabledPlatform.c_str()); + AZ_Warning(AssetBundler::AppWindowName, false, + "Platform Helper is not aware of the platform (%s).\n ", enabledPlatform.c_str()); } } @@ -457,31 +472,6 @@ namespace AssetBundler AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder)); } - AZ::Outcome GetPlatformNamesFromCacheFolder(AZStd::vector& platformNames) - { - AZ::Outcome projectCacheRootFolder = GetProjectCacheFolderPath(); - if (!projectCacheRootFolder) - { - return AZ::Failure(projectCacheRootFolder.TakeError()); - } - - const AZStd::string& projectCacheRootPath = projectCacheRootFolder.GetValue().Native(); - QDir projectCacheDir(QString::fromUtf8(projectCacheRootPath.c_str(), aznumeric_cast(projectCacheRootPath.size()))); - auto tempPlatformList = projectCacheDir.entryList(QDir::Filter::Dirs | QDir::Filter::NoDotAndDotDot); - - if (tempPlatformList.empty()) - { - return AZ::Failure(AZStd::string("Cache is empty. Please run the Open 3D Engine Asset Processor to generate a Cache and build assets.")); - } - - for (const QString& platform : tempPlatformList) - { - platformNames.push_back(AZStd::string(platform.toUtf8().data())); - } - - return AZ::Success(); - } - AZ::Outcome GetAssetCatalogFilePath() { AZ::IO::Path assetCatalogFilePath = GetPlatformSpecificCacheFolderPath(); @@ -501,7 +491,9 @@ namespace AssetBundler AZ::IO::Path platformSpecificCacheFolderPath; if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { - settingsRegistry->Get(platformSpecificCacheFolderPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder); + settingsRegistry->Get( + platformSpecificCacheFolderPath.Native(), + AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder); } return platformSpecificCacheFolderPath; } @@ -701,7 +693,8 @@ namespace AssetBundler m_errors.swap(AZStd::vector()); } - AZ::Outcome ParseComparisonType(const AZStd::string& comparisonType) + AZ::Outcome ParseComparisonType( + const AZStd::string& comparisonType) { using namespace AzToolsFramework; @@ -729,7 +722,8 @@ namespace AssetBundler } // Failure case - AZStd::string failureMessage = AZStd::string::format("Invalid Comparison Type ( %s ). Valid types are: ", comparisonType.c_str()); + AZStd::string failureMessage = AZStd::string::format( + "Invalid Comparison Type ( %s ). Valid types are: ", comparisonType.c_str()); for (size_t i = 0; i < numTypes - 1; ++i) { failureMessage.append(AZStd::string::format("%s, ", AssetFileInfoListComparison::ComparisonTypeNames[i])); @@ -738,7 +732,8 @@ namespace AssetBundler return AZ::Failure(failureMessage); } - AZ::Outcome ParseFilePatternType(const AZStd::string& filePatternType) + AZ::Outcome ParseFilePatternType( + const AZStd::string& filePatternType) { using namespace AzToolsFramework; @@ -766,7 +761,8 @@ namespace AssetBundler } // Failure case - AZStd::string failureMessage = AZStd::string::format("Invalid File Pattern Type ( %s ). Valid types are: ", filePatternType.c_str()); + AZStd::string failureMessage = AZStd::string::format( + "Invalid File Pattern Type ( %s ). Valid types are: ", filePatternType.c_str()); for (size_t i = 0; i < numTypes - 1; ++i) { failureMessage.append(AZStd::string::format("%s, ", AssetFileInfoListComparison::FilePatternTypeNames[i])); diff --git a/Code/Tools/AssetBundler/source/utils/utils.h b/Code/Tools/AssetBundler/source/utils/utils.h index 454a279878..211096e07c 100644 --- a/Code/Tools/AssetBundler/source/utils/utils.h +++ b/Code/Tools/AssetBundler/source/utils/utils.h @@ -158,17 +158,6 @@ namespace AssetBundler */ AZ::Outcome GetProjectCacheFolderPath(); - /** - * Calculates the list of enabled platforms for the input project by reading the folder names inside the project-specific cache folder. - * If the Asset Processor has not been run yet, or has not been run since the enabled platform list inside AssetProcessorPlatformConfig.setreg - * was changed, the output of this function will be incorrect. - * - * @param projectCacheFolder The directory of a project-specific cache folder: /ProjectPath/Cache - * @param platformNames [out] The list of platforms enabled in the project - * @return void on success, error message on failure - */ - AZ::Outcome GetPlatformNamesFromCacheFolder(AZStd::vector& platformNames); - /** * Computes the absolute path to the Asset Catalog file for a specified project and platform. * With platform set as "pc" and project as "ProjectName", the path will resemble: C:/ProjectPath/Cache/pc/assetcatalog.xml @@ -204,8 +193,11 @@ namespace AssetBundler AzFramework::PlatformFlags GetPlatformsOnDiskForPlatformSpecificFile(const AZStd::string& platformIndependentAbsolutePath); //! Returns a map of of all default Seed List files for the current game project. - AZStd::unordered_map GetDefaultSeedListFiles(AZStd::string_view enginePath, AZStd::string_view projectPath, - const AZStd::vector& gemInfoList, AzFramework::PlatformFlags platformFlags); + AZStd::unordered_map GetDefaultSeedListFiles( + AZStd::string_view enginePath, + AZStd::string_view projectPath, + const AZStd::vector& gemInfoList, + AzFramework::PlatformFlags platformFlags); //! Returns a vector of relative paths to Assets that should be included as default Seeds, but are not already in a Seed List file. AZStd::vector GetDefaultSeeds(AZStd::string_view projectPath, AZStd::string_view projectName); @@ -217,15 +209,24 @@ namespace AssetBundler AZ::IO::Path GetProjectDependenciesAssetPath(AZStd::string_view projectPath, AZStd::string_view projectName); //! Returns the map from gem seed list file path to gem name - AZStd::unordered_map GetGemSeedListFilePathToGemNameMap(const AZStd::vector& gemInfoList, AzFramework::PlatformFlags platformFlags); + AZStd::unordered_map GetGemSeedListFilePathToGemNameMap( + const AZStd::vector& gemInfoList, + AzFramework::PlatformFlags platformFlags); //! Given an absolute gem seed file path determines whether the file is valid for the current game project. //! This method is for validating gem seed list files only. - bool IsGemSeedFilePathValid(AZStd::string_view enginePath, AZStd::string seedAbsoluteFilePath, const AZStd::vector& gemInfoList, AzFramework::PlatformFlags platformFlags); + bool IsGemSeedFilePathValid( + AZStd::string_view enginePath, + AZStd::string seedAbsoluteFilePath, + const AZStd::vector& gemInfoList, + AzFramework::PlatformFlags platformFlags); //! Returns platformFlags of all enabled platforms by parsing all the asset processor config files. //! Please note that the game project could be in a different location to the engine therefore we need the assetRoot param. - AzFramework::PlatformFlags GetEnabledPlatformFlags(AZStd::string_view enginePath, AZStd::string_view assetRoot, AZStd::string_view projectPath); + AzFramework::PlatformFlags GetEnabledPlatformFlags( + AZStd::string_view enginePath, + AZStd::string_view assetRoot, + AZStd::string_view projectPath); QJsonObject ReadJson(const AZStd::string& filePath); void SaveJson(const AZStd::string& filePath, const QJsonObject& jsonObject); @@ -239,7 +240,11 @@ namespace AssetBundler { public: AZ_CLASS_ALLOCATOR(FilePath, AZ::SystemAllocator, 0); - explicit FilePath(const AZStd::string& filePath, AZStd::string platformIdentifier = AZStd::string(), bool checkFileCase = false, bool ignoreFileCase = false); + explicit FilePath( + const AZStd::string& filePath, + AZStd::string platformIdentifier = AZStd::string(), + bool checkFileCase = false, + bool ignoreFileCase = false); explicit FilePath(const AZStd::string& filePath, bool checkFileCase, bool ignoreFileCase); FilePath() = default; const AZStd::string& AbsolutePath() const; @@ -279,8 +284,10 @@ namespace AssetBundler bool m_reportingError = false; }; - AZ::Outcome ParseComparisonType(const AZStd::string& comparisonType); - AZ::Outcome ParseFilePatternType(const AZStd::string& filePatternType); + AZ::Outcome ParseComparisonType( + const AZStd::string& comparisonType); + AZ::Outcome ParseFilePatternType( + const AZStd::string& filePatternType); bool LooksLikePath(const AZStd::string& inputString); bool LooksLikeWildcardPattern(const AZStd::string& inputPattern); } 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..6ceb443df8 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalog.cpp @@ -0,0 +1,103 @@ +/* + * 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) + { + ConnectSlotsAndSignals(); + + 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::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, + true)); + + m_gemModel->AddGem(O3DE::ProjectManager::GemInfo("PhysX", + "O3DE Foundation", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + GemInfo::Android | GemInfo::Linux, + false)); + + m_gemModel->AddGem(O3DE::ProjectManager::GemInfo("Certificate Manager", + "O3DE Foundation", + "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 Foundation", + "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, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + 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..e5aa9c41f7 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h @@ -0,0 +1,55 @@ +/* +* 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 = 0x0, + iOS = 0x1, + Linux = 0x2, + macOS = 0x3, + Windows = 0x4 + }; + 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..22e77ed40e --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -0,0 +1,135 @@ +/* +* 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) + { + } + + 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_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); + + 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_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); + } +} // 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..3528d07d78 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h @@ -0,0 +1,62 @@ +/* +* 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" +#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; + + 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"); + + // Item + inline constexpr static int s_height = 140; // 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; + }; +} // 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..89e629cf5f --- /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(); + } + + int 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..33ae02dc8a --- /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; + int 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_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/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/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/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) { } }