diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt similarity index 75% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/CMakeLists.txt rename to AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt index 117c710824..02aaa42597 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt @@ -13,9 +13,9 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedTesting IN_LIST LY_PROJECTS) ly_add_pytest( - NAME AutomatedTesting::AtomRenderer_HydraTests_Main + NAME AutomatedTesting::Atom_TestSuite_Main TEST_SUITE main - PATH ${CMAKE_CURRENT_LIST_DIR}/test_Atom_MainSuite.py + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py TEST_SERIAL TIMEOUT 600 RUNTIME_DEPENDENCIES @@ -26,9 +26,9 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT Atom ) ly_add_pytest( - NAME AutomatedTesting::AtomRenderer_HydraTests_MainOptimized + NAME AutomatedTesting::Atom_TestSuite_Main_Optimized TEST_SUITE main - PATH ${CMAKE_CURRENT_LIST_DIR}/test_Atom_MainSuite_Optimized.py + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_Optimized.py TEST_SERIAL TIMEOUT 600 RUNTIME_DEPENDENCIES @@ -39,9 +39,9 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT Atom ) ly_add_pytest( - NAME AutomatedTesting::AtomRenderer_HydraTests_Sandbox + NAME AutomatedTesting::Atom_TestSuite_Sandbox TEST_SUITE sandbox - PATH ${CMAKE_CURRENT_LIST_DIR}/test_Atom_SandboxSuite.py + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Sandbox.py TEST_SERIAL TIMEOUT 400 RUNTIME_DEPENDENCIES @@ -52,12 +52,12 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT Atom ) ly_add_pytest( - NAME AutomatedTesting::AtomRenderer_HydraTests_GPUTests + NAME AutomatedTesting::Atom_TestSuite_Main_GPU TEST_SUITE main TEST_REQUIRES gpu TEST_SERIAL TIMEOUT 1200 - PATH ${CMAKE_CURRENT_LIST_DIR}/test_Atom_GPUTests.py + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_GPU.py RUNTIME_DEPENDENCIES AssetProcessor AutomatedTesting.Assets diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py similarity index 94% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py rename to AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py index 16e281e494..4c5716d365 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py @@ -13,10 +13,10 @@ import pytest import ly_test_tools.environment.file_system as file_system import editor_python_test_tools.hydra_test_utils as hydra -from atom_renderer.atom_utils.atom_constants import LIGHT_TYPES +from Atom.atom_utils.atom_constants import LIGHT_TYPES logger = logging.getLogger(__name__) -TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "atom_hydra_scripts") +TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests") @pytest.mark.parametrize("project", ["AutomatedTesting"]) @@ -38,24 +38,24 @@ class TestAtomEditorComponentsMain(object): 7. Exposure Control 8. Directional Light 9. DepthOfField - 10. Decal (Atom) + 10. Decal """ cfg_args = [level] expected_lines = [ - # Decal (Atom) Component - "Decal (Atom) Entity successfully created", - "Decal (Atom)_test: Component added to the entity: True", - "Decal (Atom)_test: Component removed after UNDO: True", - "Decal (Atom)_test: Component added after REDO: True", - "Decal (Atom)_test: Entered game mode: True", - "Decal (Atom)_test: Exit game mode: True", - "Decal (Atom) Controller|Configuration|Material: SUCCESS", - "Decal (Atom)_test: Entity is hidden: True", - "Decal (Atom)_test: Entity is shown: True", - "Decal (Atom)_test: Entity deleted: True", - "Decal (Atom)_test: UNDO entity deletion works: True", - "Decal (Atom)_test: REDO entity deletion works: True", + # Decal Component + "Decal Entity successfully created", + "Decal_test: Component added to the entity: True", + "Decal_test: Component removed after UNDO: True", + "Decal_test: Component added after REDO: True", + "Decal_test: Entered game mode: True", + "Decal_test: Exit game mode: True", + "Decal Controller|Configuration|Material: SUCCESS", + "Decal_test: Entity is hidden: True", + "Decal_test: Entity is shown: True", + "Decal_test: Entity deleted: True", + "Decal_test: UNDO entity deletion works: True", + "Decal_test: REDO entity deletion works: True", # DepthOfField Component "DepthOfField Entity successfully created", "DepthOfField_test: Component added to the entity: True", diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_GPUTests.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py similarity index 98% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_GPUTests.py rename to AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py index e62ab5e5dc..249b9c7096 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_GPUTests.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py @@ -3,8 +3,6 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. SPDX-License-Identifier: Apache-2.0 OR MIT - -Tests that require a GPU in order to run. """ import datetime @@ -22,7 +20,7 @@ import editor_python_test_tools.hydra_test_utils as hydra logger = logging.getLogger(__name__) DEFAULT_SUBFOLDER_PATH = 'user/PythonTests/Automated/Screenshots' -TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "atom_hydra_scripts") +TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests") def golden_images_directory(): diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite_Optimized.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py similarity index 56% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite_Optimized.py rename to AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py index e206e77d60..47b2204d56 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_Optimized.py @@ -15,32 +15,32 @@ from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite class TestAutomation(EditorTestSuite): class AtomEditorComponents_DecalAdded(EditorSharedTest): - from atom_renderer.atom_hydra_scripts import hydra_AtomEditorComponents_DecalAdded as test_module + from Atom.tests import hydra_AtomEditorComponents_DecalAdded as test_module class AtomEditorComponents_DepthOfFieldAdded(EditorSharedTest): - from atom_renderer.atom_hydra_scripts import hydra_AtomEditorComponents_DepthOfFieldAdded as test_module + from Atom.tests import hydra_AtomEditorComponents_DepthOfFieldAdded as test_module class AtomEditorComponents_DirectionalLightAdded(EditorSharedTest): - from atom_renderer.atom_hydra_scripts import hydra_AtomEditorComponents_DirectionalLightAdded as test_module + from Atom.tests import hydra_AtomEditorComponents_DirectionalLightAdded as test_module class AtomEditorComponents_ExposureControlAdded(EditorSharedTest): - from atom_renderer.atom_hydra_scripts import hydra_AtomEditorComponents_ExposureControlAdded as test_module + from Atom.tests import hydra_AtomEditorComponents_ExposureControlAdded as test_module class AtomEditorComponents_GlobalSkylightIBLAdded(EditorSharedTest): - from atom_renderer.atom_hydra_scripts import hydra_AtomEditorComponents_GlobalSkylightIBLAdded as test_module + from Atom.tests import hydra_AtomEditorComponents_GlobalSkylightIBLAdded as test_module class AtomEditorComponents_PhysicalSkyAdded(EditorSharedTest): - from atom_renderer.atom_hydra_scripts import hydra_AtomEditorComponents_PhysicalSkyAdded as test_module + from Atom.tests import hydra_AtomEditorComponents_PhysicalSkyAdded as test_module class AtomEditorComponents_PostFXRadiusWeightModifierAdded(EditorSharedTest): - from atom_renderer.atom_hydra_scripts import ( + from Atom.tests import ( hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded as test_module) class AtomEditorComponents_LightAdded(EditorSharedTest): - from atom_renderer.atom_hydra_scripts import hydra_AtomEditorComponents_LightAdded as test_module + from Atom.tests import hydra_AtomEditorComponents_LightAdded as test_module class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest): - from atom_renderer.atom_hydra_scripts import hydra_AtomEditorComponents_DisplayMapperAdded as test_module + from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest): - from .atom_hydra_scripts import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module + from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_SandboxSuite.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_SandboxSuite.py rename to AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Sandbox.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/__init__.py b/AutomatedTesting/Gem/PythonTests/Atom/__init__.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/__init__.py rename to AutomatedTesting/Gem/PythonTests/Atom/__init__.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/__init__.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/__init__.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/__init__.py rename to AutomatedTesting/Gem/PythonTests/Atom/atom_utils/__init__.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/atom_component_helper.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/atom_component_helper.py rename to AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/atom_constants.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/atom_constants.py rename to AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/benchmark_utils.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/benchmark_utils.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/benchmark_utils.py rename to AutomatedTesting/Gem/PythonTests/Atom/atom_utils/benchmark_utils.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/material_editor_utils.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/material_editor_utils.py rename to AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/screenshot_utils.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/screenshot_utils.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_utils/screenshot_utils.py rename to AutomatedTesting/Gem/PythonTests/Atom/atom_utils/screenshot_utils.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_1.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_1.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_1.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_1.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_2.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_2.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_2.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_2.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_3.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_3.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_3.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_3.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_4.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_4.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_4.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_4.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_5.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_5.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AreaLight_5.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/AreaLight_5.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AtomBasicLevelSetup.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/AtomBasicLevelSetup.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/AtomBasicLevelSetup.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/AtomBasicLevelSetup.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_1.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_1.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_1.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_1.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_2.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_2.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_2.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_2.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_3.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_3.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_3.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_3.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_4.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_4.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_4.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_4.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_5.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_5.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_5.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_5.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_6.ppm b/AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_6.ppm similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/golden_images/SpotLight_6.ppm rename to AutomatedTesting/Gem/PythonTests/Atom/golden_images/SpotLight_6.ppm diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/DependencyValidation.azsl.txt b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/DependencyValidation.azsl.txt similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/DependencyValidation.azsl.txt rename to AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/DependencyValidation.azsl.txt diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/DependencyValidation.shader.txt b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/DependencyValidation.shader.txt similarity index 93% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/DependencyValidation.shader.txt rename to AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/DependencyValidation.shader.txt index b0eac1783e..4439ec0352 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/DependencyValidation.shader.txt +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/DependencyValidation.shader.txt @@ -2,7 +2,7 @@ { "Source" : "DependencyValidation.azsl", - "DepthStencilState" : { + "DepthStencilState" : { "Depth" : { "Enable" : false, "CompareFunc" : "GreaterEqual" } }, @@ -22,5 +22,5 @@ } ] } - + } diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test1Color.azsli.txt b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test1Color.azsli.txt similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test1Color.azsli.txt rename to AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test1Color.azsli.txt diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test2Color.azsli.txt b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test2Color.azsli.txt similarity index 93% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test2Color.azsli.txt rename to AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test2Color.azsli.txt index 2ef946b947..565493a0ab 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test2Color.azsli.txt +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test2Color.azsli.txt @@ -12,5 +12,5 @@ float4 GetTest2Color(float4 color) { - return color * 0.5; + return color * 0.5; } diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test3Color.azsli.txt b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test3Color.azsli.txt similarity index 92% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test3Color.azsli.txt rename to AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test3Color.azsli.txt index 73b0cca434..7c1ff2be42 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/TestAssets/ShaderAssetBuilder/Test3Color.azsli.txt +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/TestAssets/ShaderAssetBuilder/Test3Color.azsli.txt @@ -12,5 +12,5 @@ float4 GetTest3Color(float4 color) { - return color * 0.13; + return color * 0.13; } diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/__init__.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/__init__.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/__init__.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/__init__.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_AddedToEntity.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py similarity index 99% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_AddedToEntity.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py index a2e950e1dc..602e7564b3 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_AddedToEntity.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py @@ -184,7 +184,7 @@ def run(): material_asset = asset.AssetCatalogRequestBus( bus.Broadcast, "GetAssetIdByPath", material_asset_path, math.Uuid(), False) ComponentTests( - "Decal (Atom)", lambda entity_obj: verify_set_property( + "Decal", lambda entity_obj: verify_set_property( entity_obj, "Controller|Configuration|Material", material_asset)) # Directional Light Component diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_DecalAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py similarity index 99% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_DecalAdded.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py index d68e1f5844..fa02b75fe2 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_DecalAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DecalAdded.py @@ -73,7 +73,7 @@ def AtomEditorComponents_Decal_AddedToEntity(): # Test steps begin. # 1. Create a Decal entity with no components. - decal_name = "Decal (Atom)" + decal_name = "Decal" decal_entity = EditorEntity.create_editor_entity_at(math.Vector3(512.0, 512.0, 34.0), decal_name) Report.critical_result(Tests.decal_creation, decal_entity.exists()) diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_DepthOfFieldAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_DepthOfFieldAdded.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DepthOfFieldAdded.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_DirectionalLightAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_DirectionalLightAdded.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DirectionalLightAdded.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_DisplayMapperAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_DisplayMapperAdded.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DisplayMapperAdded.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_ExposureControlAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_ExposureControlAdded.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_ExposureControlAdded.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_GlobalSkylightIBLAdded.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_LightAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_LightAdded.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightAdded.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_LightComponent.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py similarity index 99% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_LightComponent.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py index 6da922bd9f..751f425916 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_LightComponent.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py @@ -17,7 +17,7 @@ import azlmbr.legacy.general as general sys.path.append(os.path.join(azlmbr.paths.devassets, "Gem", "PythonTests")) import editor_python_test_tools.hydra_editor_utils as hydra -from atom_renderer.atom_utils.atom_constants import LIGHT_TYPES +from Atom.atom_utils.atom_constants import LIGHT_TYPES LIGHT_TYPE_PROPERTY = 'Controller|Configuration|Light type' SPHERE_AND_SPOT_DISK_LIGHT_PROPERTIES = [ diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_PhysicalSkyAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_PhysicalSkyAdded.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PhysicalSkyAdded.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_PostFXRadiusWeightModifierAdded.py diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomMaterialEditor_BasicTests.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py similarity index 99% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomMaterialEditor_BasicTests.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py index 1e250e5b9f..88a1ef4c7b 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomMaterialEditor_BasicTests.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py @@ -18,7 +18,7 @@ import azlmbr.paths sys.path.append(os.path.join(azlmbr.paths.devassets, "Gem", "PythonTests")) -import atom_renderer.atom_utils.material_editor_utils as material_editor +import Atom.atom_utils.material_editor_utils as material_editor NEW_MATERIAL = "test_material.material" NEW_MATERIAL_1 = "test_material_1.material" diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py similarity index 98% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py index 7e7087fb42..4f7edeba75 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py @@ -14,7 +14,7 @@ sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "P import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.editor_test_helper import EditorTestHelper -from atom_renderer.atom_utils.benchmark_utils import BenchmarkHelper +from Atom.atom_utils.benchmark_utils import BenchmarkHelper SCREEN_WIDTH = 1280 SCREEN_HEIGHT = 720 diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_BasicLevelSetup.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py similarity index 99% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_BasicLevelSetup.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py index 24ebbdff63..62a122a723 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_BasicLevelSetup.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py @@ -21,7 +21,7 @@ sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "P import editor_python_test_tools.hydra_editor_utils as hydra from editor_python_test_tools.editor_test_helper import EditorTestHelper -from atom_renderer.atom_utils.screenshot_utils import ScreenshotHelper +from Atom.atom_utils.screenshot_utils import ScreenshotHelper SCREEN_WIDTH = 1280 SCREEN_HEIGHT = 720 diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_LightComponent.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py similarity index 99% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_LightComponent.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py index 5c019dc3f3..4a3ae8c85d 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_GPUTest_LightComponent.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py @@ -17,7 +17,7 @@ import azlmbr.legacy.general as general sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests")) import editor_python_test_tools.hydra_editor_utils as hydra -from atom_renderer.atom_utils import atom_component_helper, atom_constants, screenshot_utils +from Atom.atom_utils import atom_component_helper, atom_constants, screenshot_utils from editor_python_test_tools.editor_test_helper import EditorTestHelper helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper") diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges.py similarity index 100% rename from AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges.py rename to AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges.py diff --git a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt index 7fd3b3a241..466a4b1679 100644 --- a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt @@ -18,7 +18,7 @@ include(${pal_dir}/PAL_traits_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) add_subdirectory(assetpipeline) ## Atom Renderer ## -add_subdirectory(atom_renderer) +add_subdirectory(Atom) ## Physics ## add_subdirectory(Physics) diff --git a/Code/Editor/CMakeLists.txt b/Code/Editor/CMakeLists.txt index 5158ebc6d5..5884795413 100644 --- a/Code/Editor/CMakeLists.txt +++ b/Code/Editor/CMakeLists.txt @@ -254,4 +254,35 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_add_googletest( NAME Legacy::EditorLib.Tests ) + + ly_add_target( + NAME EditorLib.Camera.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Legacy + FILES_CMAKE + Lib/Tests/Camera/editor_lib_camera_test_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + . + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AzTest + AZ::AzToolsFramework + AZ::AzTestShared + Legacy::EditorLib + Gem::Camera.Editor + Gem::AtomToolsFramework.Static + RUNTIME_DEPENDENCIES + Legacy::EditorLib + ) + + ly_add_source_properties( + SOURCES Lib/Tests/Camera/test_EditorCamera.cpp + PROPERTY COMPILE_DEFINITIONS + VALUES CAMERA_EDITOR_MODULE="$" + ) + + ly_add_googletest( + NAME Legacy::EditorLib.Camera.Tests + ) endif() diff --git a/Code/Editor/EditorModularViewportCameraComposer.cpp b/Code/Editor/EditorModularViewportCameraComposer.cpp index 498d6f3353..e375e98fb6 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.cpp +++ b/Code/Editor/EditorModularViewportCameraComposer.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -34,10 +35,12 @@ namespace SandboxEditor : m_viewportId(viewportId) { EditorModularViewportCameraComposerNotificationBus::Handler::BusConnect(viewportId); + Camera::EditorCameraNotificationBus::Handler::BusConnect(); } EditorModularViewportCameraComposer::~EditorModularViewportCameraComposer() { + Camera::EditorCameraNotificationBus::Handler::BusDisconnect(); EditorModularViewportCameraComposerNotificationBus::Handler::BusDisconnect(); } @@ -283,4 +286,22 @@ namespace SandboxEditor m_orbitCamera->SetOrbitInputChannelId(SandboxEditor::CameraOrbitChannelId()); m_orbitDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraOrbitDollyChannelId()); } + + void EditorModularViewportCameraComposer::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) + { + if (viewEntityId.IsValid()) + { + AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); + AZ::TransformBus::EventResult(worldFromLocal, viewEntityId, &AZ::TransformBus::Events::GetWorldTM); + + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, + worldFromLocal); + } + else + { + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame); + } + } } // namespace SandboxEditor diff --git a/Code/Editor/EditorModularViewportCameraComposer.h b/Code/Editor/EditorModularViewportCameraComposer.h index cb223d39e6..e6e71c976c 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.h +++ b/Code/Editor/EditorModularViewportCameraComposer.h @@ -10,13 +10,16 @@ #include #include +#include #include #include namespace SandboxEditor { //! Type responsible for building the editor's modular viewport camera controller. - class EditorModularViewportCameraComposer : private EditorModularViewportCameraComposerNotificationBus::Handler + class EditorModularViewportCameraComposer + : private EditorModularViewportCameraComposerNotificationBus::Handler + , private Camera::EditorCameraNotificationBus::Handler { public: SANDBOX_API explicit EditorModularViewportCameraComposer(AzFramework::ViewportId viewportId); @@ -32,6 +35,9 @@ namespace SandboxEditor // EditorModularViewportCameraComposerNotificationBus overrides ... void OnEditorModularViewportCameraComposerSettingsChanged() override; + // EditorCameraNotificationBus overrides ... + void OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) override; + AZStd::shared_ptr m_firstPersonRotateCamera; AZStd::shared_ptr m_firstPersonPanCamera; AZStd::shared_ptr m_firstPersonTranslateCamera; diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Android/tool_dependencies_android.cmake b/Code/Editor/Lib/Tests/Camera/editor_lib_camera_test_files.cmake similarity index 85% rename from Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Android/tool_dependencies_android.cmake rename to Code/Editor/Lib/Tests/Camera/editor_lib_camera_test_files.cmake index 5bf4d7cb7e..69d3e37f2d 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Android/tool_dependencies_android.cmake +++ b/Code/Editor/Lib/Tests/Camera/editor_lib_camera_test_files.cmake @@ -6,5 +6,6 @@ # # -set(GEM_DEPENDENCIES +set(FILES + test_EditorCamera.cpp ) diff --git a/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp b/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp new file mode 100644 index 0000000000..affe5794cc --- /dev/null +++ b/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + class EditorCameraTestEnvironment : public AZ::Test::GemTestEnvironment + { + // AZ::Test::GemTestEnvironment overrides ... + void AddGemsAndComponents() override; + }; + + void EditorCameraTestEnvironment::AddGemsAndComponents() + { + AddDynamicModulePaths({ CAMERA_EDITOR_MODULE }); + AddComponentDescriptors({ AzToolsFramework::Components::TransformComponent::CreateDescriptor() }); + } + + class EditorCameraFixture : public ::testing::Test + { + public: + AtomToolsFramework::ModularCameraViewportContext* m_cameraViewportContextView = nullptr; + AZStd::unique_ptr m_editorModularViewportCameraComposer; + AZStd::unique_ptr m_editorLibHandle; + AzFramework::ViewportControllerListPtr m_controllerList; + AZStd::unique_ptr m_entity; + + static const AzFramework::ViewportId TestViewportId; + + void SetUp() override + { + m_editorLibHandle = AZ::DynamicModuleHandle::Create("EditorLib"); + [[maybe_unused]] const bool loaded = m_editorLibHandle->Load(true); + AZ_Assert(loaded, "EditorLib could not be loaded"); + + m_controllerList = AZStd::make_shared(); + m_controllerList->RegisterViewportContext(TestViewportId); + + m_entity = AZStd::make_unique(); + m_entity->Init(); + m_entity->CreateComponent(); + m_entity->Activate(); + + m_editorModularViewportCameraComposer = AZStd::make_unique(TestViewportId); + + auto controller = m_editorModularViewportCameraComposer->CreateModularViewportCameraController(); + // set some overrides for the test + controller->SetCameraViewportContextBuilderCallback( + [this](AZStd::unique_ptr& cameraViewportContext) mutable + { + cameraViewportContext = AZStd::make_unique(); + m_cameraViewportContextView = cameraViewportContext.get(); + }); + + m_controllerList->Add(controller); + } + + void TearDown() override + { + m_editorModularViewportCameraComposer.reset(); + m_cameraViewportContextView = nullptr; + m_entity.reset(); + m_editorLibHandle = {}; + } + }; + + const AzFramework::ViewportId EditorCameraFixture::TestViewportId = AzFramework::ViewportId(1337); + + TEST_F(EditorCameraFixture, ModularViewportCameraControllerReferenceFrameUpdatedWhenViewportEntityisChanged) + { + // Given + const auto entityTransform = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(10.0f, 5.0f, -2.0f)); + AZ::TransformBus::Event(m_entity->GetId(), &AZ::TransformBus::Events::SetWorldTM, entityTransform); + + // When + // imitate viewport entity changing + Camera::EditorCameraNotificationBus::Broadcast( + &Camera::EditorCameraNotificationBus::Events::OnViewportViewEntityChanged, m_entity->GetId()); + + // ensure the viewport updates after the viewport view entity change + const float deltaTime = 1.0f / 60.0f; + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() }); + + // retrieve updated camera transform + const AZ::Transform cameraTransform = m_cameraViewportContextView->GetCameraTransform(); + + // Then + // camera transform matches that of the entity + EXPECT_THAT(cameraTransform, IsClose(entityTransform)); + } + + TEST_F(EditorCameraFixture, ReferenceFrameRemainsIdentityAfterExternalCameraTransformChangeWhenNotSet) + { + // Given + m_cameraViewportContextView->SetCameraTransform(AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f))); + + // When + AZ::Transform referenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( + referenceFrame, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); + + // Then + // reference frame is still the identity + EXPECT_THAT(referenceFrame, IsClose(AZ::Transform::CreateIdentity())); + } + + TEST_F(EditorCameraFixture, ExternalCameraTransformChangeWhenReferenceFrameIsSetUpdatesReferenceFrame) + { + // Given + const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame); + + const AZ::Transform nextTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f)); + m_cameraViewportContextView->SetCameraTransform(nextTransform); + + // When + AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( + currentReferenceFrame, TestViewportId, + &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); + + // Then + EXPECT_THAT(currentReferenceFrame, IsClose(nextTransform)); + } + + TEST_F(EditorCameraFixture, ReferenceFrameReturnedToIdentityAfterClear) + { + // Given + const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame); + + // When + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame); + + AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( + currentReferenceFrame, TestViewportId, + &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); + + // Then + EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity())); + } + + TEST_F(EditorCameraFixture, InterpolateToTransform) + { + // When + AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, + transformToInterpolateTo, 0.0f); + + // simulate interpolation + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); + + const auto finalTransform = m_cameraViewportContextView->GetCameraTransform(); + + // Then + EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo)); + } + + TEST_F(EditorCameraFixture, InterpolateToTransformWithReferenceSpaceSet) + { + // Given + const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame); + + AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f)); + + // When + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, + transformToInterpolateTo, 0.0f); + + // simulate interpolation + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); + + AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( + currentReferenceFrame, TestViewportId, + &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); + + const auto finalTransform = m_cameraViewportContextView->GetCameraTransform(); + + // Then + EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo)); + EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity())); + } +} // namespace UnitTest + +// required to support running integration tests with the Camera Gem +AZTEST_EXPORT int AZ_UNIT_TEST_HOOK_NAME(int argc, char** argv) +{ + ::testing::InitGoogleMock(&argc, argv); + AZ::Test::printUnusedParametersWarning(argc, argv); + AZ::Test::addTestEnvironments({ new UnitTest::EditorCameraTestEnvironment() }); + int result = RUN_ALL_TESTS(); + return result; +} + +IMPLEMENT_TEST_EXECUTABLE_MAIN(); diff --git a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp index ce5564c7f6..157291cfc2 100644 --- a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp +++ b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp @@ -58,28 +58,6 @@ namespace UnitTest return true; } - class TestModularCameraViewportContextImpl : public AtomToolsFramework::ModularCameraViewportContext - { - public: - AZ::Transform GetCameraTransform() const override - { - return m_cameraTransform; - } - - void SetCameraTransform(const AZ::Transform& transform) override - { - m_cameraTransform = transform; - } - - void ConnectViewMatrixChangedHandler(AZ::RPI::ViewportContext::MatrixChangedEvent::Handler&) override - { - // noop - } - - private: - AZ::Transform m_cameraTransform = AZ::Transform::CreateIdentity(); - }; - class ModularViewportCameraControllerFixture : public AllocatorsTestFixture { public: @@ -146,7 +124,7 @@ namespace UnitTest controller->SetCameraViewportContextBuilderCallback( [this](AZStd::unique_ptr& cameraViewportContext) { - cameraViewportContext = AZStd::make_unique(); + cameraViewportContext = AZStd::make_unique(); m_cameraViewportContextView = cameraViewportContext.get(); }); diff --git a/Code/Framework/AzCore/AzCore/Math/Transform.cpp b/Code/Framework/AzCore/AzCore/Math/Transform.cpp index 5cb09fe9a8..1701820bae 100644 --- a/Code/Framework/AzCore/AzCore/Math/Transform.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Transform.cpp @@ -353,6 +353,7 @@ namespace AZ Method("CreateFromMatrix3x3AndTranslation", &Transform::CreateFromMatrix3x3AndTranslation)-> Method("CreateUniformScale", &Transform::CreateUniformScale)-> Method("CreateTranslation", &Transform::CreateTranslation)-> + Method("CreateLookAt", &Transform::CreateLookAt)-> Method("ConstructFromValuesNumeric", &Internal::ConstructTransformFromValues); } } diff --git a/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake b/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake index dc77ca0abf..5c8ca8392f 100644 --- a/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake +++ b/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake @@ -10,17 +10,12 @@ # Only 'xcb' and 'wayland' are recognized if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb") - find_library(XCB_LIBRARY xcb) - find_library(XCB_XKB_LIBRARY xcb-xkb) - find_library(XKBCOMMON_LIBRARY xkbcommon) - find_library(XKBCOMMON_X11_LIBRARY xkbcommon-x11) - set(LY_BUILD_DEPENDENCIES PRIVATE - ${XCB_LIBRARY} - ${XKBCOMMON_LIBRARY} - ${XKBCOMMON_X11_LIBRARY} - ${XCB_XKB_LIBRARY} + 3rdParty::X11::xcb + 3rdParty::X11::xcb_xkb + 3rdParty::X11::xkbcommon + 3rdParty::X11::xkbcommon_X11 ) set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp index 75d31321c2..91a916ae70 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -248,6 +249,7 @@ namespace AzToolsFramework components.insert(components.end(), { azrtti_typeid(), azrtti_typeid(), + azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp index fdbd4287ce..469a235b9f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.cpp @@ -46,7 +46,7 @@ namespace AzToolsFramework QModelIndex AssetBrowserTableModel::mapToSource(const QModelIndex& proxyIndex) const { - Q_ASSERT(!proxyIndex.isValid() || proxyIndex.model() != this); + Q_ASSERT(!proxyIndex.isValid() || proxyIndex.model() == this); if (!proxyIndex.isValid() || !m_indexMap.contains(proxyIndex.row())) { return QModelIndex(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp index 97f52e253b..b68d086892 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,7 @@ namespace AzToolsFramework Components::EditorSelectionAccentSystemComponent::CreateDescriptor(), EditorEntityContextComponent::CreateDescriptor(), EditorEntityFixupComponent::CreateDescriptor(), + FocusModeSystemComponent::CreateDescriptor(), SliceMetadataEntityContextComponent::CreateDescriptor(), SliceRequestComponent::CreateDescriptor(), Prefab::PrefabSystemComponent::CreateDescriptor(), diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h new file mode 100644 index 0000000000..6d3a7599de --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace AzToolsFramework +{ + //! FocusModeInterface + //! Interface to handle the Editor Focus Mode. + class FocusModeInterface + { + public: + AZ_RTTI(FocusModeInterface, "{437243B0-F86B-422F-B7B8-4A21CC000702}"); + + //! Sets the root entity the Editor should focus on. + //! The Editor will only allow the user to select entities that are descendants of the EntityId provided. + //! @param entityId The entityId that will become the new focus root. + virtual void SetFocusRoot(AZ::EntityId entityId) = 0; + + //! Clears the Editor focus, allowing the user to select the whole level again. + virtual void ClearFocusRoot() = 0; + + //! Returns the entity id of the root of the current Editor focus. + //! @return The entity id of the root of the Editor focus, or an invalid entity id if no focus is set. + virtual AZ::EntityId GetFocusRoot() = 0; + + //! Returns whether the entity id provided is part of the focused sub-tree. + virtual bool IsInFocusSubTree(AZ::EntityId entityId) = 0; + }; + +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp new file mode 100644 index 0000000000..e08191032d --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include + +namespace AzToolsFramework +{ + bool IsInFocusSubTree(AZ::EntityId entityId, AZ::EntityId focusRootId) + { + if (entityId == AZ::EntityId()) + { + return false; + } + + if (entityId == focusRootId) + { + return true; + } + + AZ::EntityId parentId; + AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformInterface::GetParentId); + + return IsInFocusSubTree(parentId, focusRootId); + } + + void FocusModeSystemComponent::Init() + { + } + + void FocusModeSystemComponent::Activate() + { + AZ::Interface::Register(this); + } + + void FocusModeSystemComponent::Deactivate() + { + AZ::Interface::Unregister(this); + } + + void FocusModeSystemComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context) + { + } + + void FocusModeSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("EditorFocusMode")); + } + + void FocusModeSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + } + + void FocusModeSystemComponent::GetIncompatibleServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + } + + void FocusModeSystemComponent::SetFocusRoot(AZ::EntityId entityId) + { + m_focusRoot = entityId; + + // TODO - If m_focusRoot != AZ::EntityId(), activate focus mode via ViewportEditorModeTrackerInterface; else, deactivate focus mode + } + + void FocusModeSystemComponent::ClearFocusRoot() + { + SetFocusRoot(AZ::EntityId()); + } + + AZ::EntityId FocusModeSystemComponent::GetFocusRoot() + { + return m_focusRoot; + } + + bool FocusModeSystemComponent::IsInFocusSubTree(AZ::EntityId entityId) + { + if (m_focusRoot == AZ::EntityId()) + { + return true; + } + + return AzToolsFramework::IsInFocusSubTree(entityId, m_focusRoot); + } + +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h new file mode 100644 index 0000000000..697a4ff998 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +#include + +namespace AzToolsFramework +{ + bool IsInFocusSubTree(AZ::EntityId entityId, AZ::EntityId focusRootId); + + //! System Component to handle the Editor Focus Mode system + class FocusModeSystemComponent final + : public AZ::Component + , private FocusModeInterface + { + public: + AZ_COMPONENT(FocusModeSystemComponent, "{6CE522FE-2057-4794-BD05-61E04BD8EA30}"); + + FocusModeSystemComponent() = default; + virtual ~FocusModeSystemComponent() = default; + + // AZ::Component overrides ... + void Init() override; + void Activate() override; + void Deactivate() override; + + static void Reflect(AZ::ReflectContext* context); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + + // FocusModeInterface overrides ... + void SetFocusRoot(AZ::EntityId entityId) override; + void ClearFocusRoot() override; + AZ::EntityId GetFocusRoot() override; + bool IsInFocusSubTree(AZ::EntityId entityId) override; + + private: + AZ::EntityId m_focusRoot; + }; + +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp new file mode 100644 index 0000000000..4fd202a73d --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include +#include + +namespace AzToolsFramework::Prefab +{ + PrefabFocusHandler::PrefabFocusHandler() + { + m_instanceEntityMapperInterface = AZ::Interface::Get(); + AZ_Assert( + m_instanceEntityMapperInterface, + "Prefab - PrefabFocusHandler - " + "Instance Entity Mapper Interface could not be found. " + "Check that it is being correctly initialized."); + + AZ::Interface::Register(this); + } + + PrefabFocusHandler::~PrefabFocusHandler() + { + AZ::Interface::Unregister(this); + } + + PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId) + { + InstanceOptionalReference focusedInstance; + + if (entityId == AZ::EntityId()) + { + PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface = + AZ::Interface::Get(); + + if(!prefabEditorEntityOwnershipInterface) + { + return AZ::Failure(AZStd::string("Could not focus on root prefab instance - internal error " + "(PrefabEditorEntityOwnershipInterface unavailable).")); + } + + focusedInstance = prefabEditorEntityOwnershipInterface->GetRootPrefabInstance(); + } + else + { + focusedInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); + } + + if (!focusedInstance.has_value()) + { + return AZ::Failure(AZStd::string( + "Prefab Focus Handler: Couldn't find owning instance of entityId provided.")); + } + + m_focusedInstance = focusedInstance; + m_focusedTemplateId = focusedInstance->get().GetTemplateId(); + + FocusModeInterface* focusModeInterface = AZ::Interface::Get(); + if (focusModeInterface) + { + focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId()); + } + + return AZ::Success(); + } + + TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId() + { + return m_focusedTemplateId; + } + + InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance() + { + return m_focusedInstance; + } + + bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) + { + if (!m_focusedInstance.has_value()) + { + // PrefabFocusHandler has not been initialized yet. + return false; + } + + if (entityId == AZ::EntityId()) + { + return false; + } + + InstanceOptionalReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); + + return instance.has_value() && (&instance->get() == &m_focusedInstance->get()); + } + +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h new file mode 100644 index 0000000000..fa7727894d --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include +#include +#include + +namespace AzToolsFramework::Prefab +{ + class InstanceEntityMapperInterface; + + //! Handles Prefab Focus mode, determining which prefab file entity changes will target. + class PrefabFocusHandler final + : private PrefabFocusInterface + { + public: + AZ_CLASS_ALLOCATOR(PrefabFocusHandler, AZ::SystemAllocator, 0); + + PrefabFocusHandler(); + ~PrefabFocusHandler(); + + // PrefabFocusInterface override ... + PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override; + TemplateId GetFocusedPrefabTemplateId() override; + InstanceOptionalReference GetFocusedPrefabInstance() override; + bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) override; + + private: + InstanceOptionalReference m_focusedInstance; + TemplateId m_focusedTemplateId; + + InstanceEntityMapperInterface* m_instanceEntityMapperInterface; + }; + +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h new file mode 100644 index 0000000000..833a5ef7c8 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +#include +#include + +namespace AzToolsFramework::Prefab +{ + using PrefabFocusOperationResult = AZ::Outcome; + + //! Interface to handle operations related to the Prefab Focus system. + class PrefabFocusInterface + { + public: + AZ_RTTI(PrefabFocusInterface, "{F3CFA37B-5FD8-436A-9C30-60EB54E350E1}"); + + //! Set the focused prefab instance to the owning instance of the entityId provided. + //! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on. + virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0; + + //! Returns the template id of the instance the prefab system is focusing on. + virtual TemplateId GetFocusedPrefabTemplateId() = 0; + + //! Returns a reference to the instance the prefab system is focusing on. + virtual InstanceOptionalReference GetFocusedPrefabInstance() = 0; + + //! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants. + //! @param entityId The entityId of the queried entity. + //! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise. + virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) = 0; + }; + +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 42b6bc1666..f3c0e67d46 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -189,6 +190,9 @@ namespace AzToolsFramework PrefabLoaderInterface* m_prefabLoaderInterface = nullptr; PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr; + // Handles the Prefab Focus API that determines what prefab is being edited. + PrefabFocusHandler m_prefabFocusHandler; + // Caches entity states for undo/redo purposes PrefabUndoCache m_prefabUndoCache; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h index d6908922c9..746c306c2b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h @@ -369,7 +369,7 @@ namespace AzToolsFramework // A counter for generating unique Link Ids. AZStd::atomic m_linkIdCounter = 0u; - // Used for finding the owning instance of an arbitrary entity + // Used for finding the owning instance of an arbitrary entity. InstanceEntityMapper m_instanceEntityMapper; // Used for finding the Instances owned by an arbitrary Template. @@ -378,16 +378,16 @@ namespace AzToolsFramework // Used for loading/saving Prefab Template files. PrefabLoader m_prefabLoader; - // Handler the public Prefab API used by UI and scripting + // Handles the public Prefab API used by UI and scripting. PrefabPublicHandler m_prefabPublicHandler; // Used for updating Instances of Prefab Template. InstanceUpdateExecutor m_instanceUpdateExecutor; - // Used for updating Templates when Instances are modified + // Used for updating Templates when Instances are modified. InstanceToTemplatePropagator m_instanceToTemplatePropagator; - // Handler of the public Prefab requests + // Handler of the public Prefab requests. PrefabPublicRequestHandler m_prefabPublicRequestHandler; }; } // namespace Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp index c75f6aa86d..bb6bdf0ebd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp @@ -8,7 +8,6 @@ #include -#include #include #include @@ -24,14 +23,6 @@ namespace AzToolsFramework LevelRootUiHandler::LevelRootUiHandler() { - m_prefabEditInterface = AZ::Interface::Get(); - - if (m_prefabEditInterface == nullptr) - { - AZ_Assert(false, "LevelRootUiHandler - could not get PrefabEditInterface on LevelRootUiHandler construction."); - return; - } - m_prefabPublicInterface = AZ::Interface::Get(); if (m_prefabPublicInterface == nullptr) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h index 6eeccfe88d..1e485572b8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h @@ -14,7 +14,6 @@ namespace AzToolsFramework { namespace Prefab { - class PrefabEditInterface; class PrefabPublicInterface; }; @@ -36,7 +35,6 @@ namespace AzToolsFramework void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; private: - Prefab::PrefabEditInterface* m_prefabEditInterface = nullptr; Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr; static constexpr int m_levelRootBorderThickness = 1; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditInterface.h deleted file mode 100644 index 1bd4a164f8..0000000000 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditInterface.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include - -namespace AzToolsFramework -{ - namespace Prefab - { - /*! - * PrefabEditInterface - * Interface to expose the API to Edit Prefabs in the Editor. - */ - class PrefabEditInterface - { - public: - AZ_RTTI(PrefabEditInterface, "{DABB1D43-3760-420E-9F1E-5104F0AFF167}"); - - /** - * Sets the prefab for the instance owning the entity provided as the prefab being edited. - * @param entityId The entity whose owning prefab should be edited. - */ - virtual void EditOwningPrefab(AZ::EntityId entityId) = 0; - - /** - * Queries the Edit Manager to know if the provided entity is part of the prefab currently being edited. - * @param entityId The entity whose prefab editing state we want to query. - * @return True if the prefab owning this entity is being edited, false otherwise. - */ - virtual bool IsOwningPrefabBeingEdited(AZ::EntityId entityId) = 0; - }; - - } // namespace Prefab -} // namespace AzToolsFramework - diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditManager.cpp deleted file mode 100644 index 27a4c3bcaf..0000000000 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditManager.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include - -#include - -namespace AzToolsFramework -{ - namespace Prefab - { - PrefabEditManager::PrefabEditManager() - { - m_prefabPublicInterface = AZ::Interface::Get(); - - if (m_prefabPublicInterface == nullptr) - { - AZ_Assert(false, "Prefab - could not get PrefabPublicInterface on PrefabEditManager construction."); - return; - } - - AZ::Interface::Register(this); - } - - PrefabEditManager::~PrefabEditManager() - { - AZ::Interface::Unregister(this); - } - - void PrefabEditManager::EditOwningPrefab(AZ::EntityId entityId) - { - m_instanceBeingEdited = m_prefabPublicInterface->GetInstanceContainerEntityId(entityId); - } - - bool PrefabEditManager::IsOwningPrefabBeingEdited(AZ::EntityId entityId) - { - AZ::EntityId containerEntity = m_prefabPublicInterface->GetInstanceContainerEntityId(entityId); - return m_instanceBeingEdited == containerEntity; - } - } -} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditManager.h deleted file mode 100644 index d155322e3c..0000000000 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabEditManager.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include - -#include -#include - -namespace AzToolsFramework -{ - namespace Prefab - { - class PrefabEditManager final - : private PrefabEditInterface - { - public: - AZ_CLASS_ALLOCATOR(PrefabEditManager, AZ::SystemAllocator, 0); - - PrefabEditManager(); - ~PrefabEditManager(); - - private: - // PrefabEditInterface... - void EditOwningPrefab(AZ::EntityId entityId) override; - bool IsOwningPrefabBeingEdited(AZ::EntityId entityId) override; - - AZ::EntityId m_instanceBeingEdited; - - PrefabPublicInterface* m_prefabPublicInterface; - }; - } -} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 0c61ccb501..9b7f2fff10 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -57,9 +58,9 @@ namespace AzToolsFramework { EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr; - PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr; - PrefabEditInterface* PrefabIntegrationManager::s_prefabEditInterface = nullptr; + PrefabFocusInterface* PrefabIntegrationManager::s_prefabFocusInterface = nullptr; PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr; + PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr; PrefabSystemComponentInterface* PrefabIntegrationManager::s_prefabSystemComponentInterface = nullptr; const AZStd::string PrefabIntegrationManager::s_prefabFileExtension = ".prefab"; @@ -102,13 +103,6 @@ namespace AzToolsFramework return; } - s_prefabEditInterface = AZ::Interface::Get(); - if (s_prefabEditInterface == nullptr) - { - AZ_Assert(false, "Prefab - could not get PrefabEditInterface on PrefabIntegrationManager construction."); - return; - } - s_prefabLoaderInterface = AZ::Interface::Get(); if (s_prefabLoaderInterface == nullptr) { @@ -123,6 +117,13 @@ namespace AzToolsFramework return; } + s_prefabFocusInterface = AZ::Interface::Get(); + if (s_prefabFocusInterface == nullptr) + { + AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabIntegrationManager construction."); + return; + } + EditorContextMenuBus::Handler::BusConnect(); PrefabInstanceContainerNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); @@ -224,7 +225,7 @@ namespace AzToolsFramework // Edit Prefab if (prefabWipFeaturesEnabled) { - bool beingEdited = s_prefabEditInterface->IsOwningPrefabBeingEdited(selectedEntity); + bool beingEdited = s_prefabFocusInterface->IsOwningPrefabBeingFocused(selectedEntity); if (!beingEdited) { @@ -428,7 +429,7 @@ namespace AzToolsFramework void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity) { - s_prefabEditInterface->EditOwningPrefab(containerEntity); + s_prefabFocusInterface->FocusOnOwningPrefab(containerEntity); } void PrefabIntegrationManager::ContextMenu_SavePrefab(AZ::EntityId containerEntity) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h index 3e66350932..6f66b1f514 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h @@ -17,7 +17,7 @@ #include #include #include -#include + #include #include #include @@ -28,7 +28,7 @@ namespace AzToolsFramework { namespace Prefab { - + class PrefabFocusInterface; class PrefabLoaderInterface; //! Structure for saving/retrieving user settings related to prefab workflows. @@ -80,9 +80,6 @@ namespace AzToolsFramework void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override; private: - // Manages the Edit Mode UI for prefabs - PrefabEditManager m_prefabEditManager; - // Used to handle the UI for the level root LevelRootUiHandler m_levelRootUiHandler; @@ -135,13 +132,12 @@ namespace AzToolsFramework AZStd::unique_ptr ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference); void SavePrefabsInDialog(QDialog* unsavedPrefabsDialog); - static const AZStd::string s_prefabFileExtension; static EditorEntityUiInterface* s_editorEntityUiInterface; - static PrefabPublicInterface* s_prefabPublicInterface; - static PrefabEditInterface* s_prefabEditInterface; + static PrefabFocusInterface* s_prefabFocusInterface; static PrefabLoaderInterface* s_prefabLoaderInterface; + static PrefabPublicInterface* s_prefabPublicInterface; static PrefabSystemComponentInterface* s_prefabSystemComponentInterface; }; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp index fda56b79f1..c802850c4b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include @@ -26,21 +26,19 @@ namespace AzToolsFramework PrefabUiHandler::PrefabUiHandler() { - m_prefabEditInterface = AZ::Interface::Get(); - - if (m_prefabEditInterface == nullptr) - { - AZ_Assert(false, "PrefabUiHandler - could not get PrefabEditInterface on PrefabUiHandler construction."); - return; - } - m_prefabPublicInterface = AZ::Interface::Get(); - if (m_prefabPublicInterface == nullptr) { AZ_Assert(false, "PrefabUiHandler - could not get PrefabPublicInterface on PrefabUiHandler construction."); return; } + + m_prefabFocusInterface = AZ::Interface::Get(); + if (m_prefabFocusInterface == nullptr) + { + AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusInterface on PrefabUiHandler construction."); + return; + } } QString PrefabUiHandler::GenerateItemInfoString(AZ::EntityId entityId) const @@ -83,7 +81,7 @@ namespace AzToolsFramework QIcon PrefabUiHandler::GenerateItemIcon(AZ::EntityId entityId) const { - if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId)) + if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId)) { return QIcon(m_prefabEditIconPath); } @@ -105,7 +103,7 @@ namespace AzToolsFramework const bool hasVisibleChildren = index.data(EntityOutlinerListModel::ExpandedRole).value() && index.model()->hasChildren(index); QColor backgroundColor = m_prefabCapsuleColor; - if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId)) + if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId)) { backgroundColor = m_prefabCapsuleEditColor; } @@ -191,7 +189,7 @@ namespace AzToolsFramework const bool isLastColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnLockToggle; QColor borderColor = m_prefabCapsuleColor; - if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId)) + if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId)) { borderColor = m_prefabCapsuleEditColor; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h index d900fae427..547c100eb1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h @@ -12,9 +12,10 @@ namespace AzToolsFramework { + namespace Prefab { - class PrefabEditInterface; + class PrefabFocusInterface; class PrefabPublicInterface; }; @@ -37,7 +38,7 @@ namespace AzToolsFramework const QModelIndex& descendantIndex) const override; private: - Prefab::PrefabEditInterface* m_prefabEditInterface = nullptr; + Prefab::PrefabFocusInterface* m_prefabFocusInterface = nullptr; Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr; static bool IsLastVisibleChild(const QModelIndex& parent, const QModelIndex& child); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 27a77f9ce7..8157d369d2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -3642,7 +3642,7 @@ namespace AzToolsFramework } } - void EditorTransformComponentSelection::OnViewportViewEntityChanged(const AZ::EntityId& newViewId) + void EditorTransformComponentSelection::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) { AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -3650,12 +3650,12 @@ namespace AzToolsFramework // match the editor camera translation/orientation), record the entity id if we have // a manipulator tracking it (entity id exists in m_entityIdManipulator lookups) // and remove it when recreating manipulators (see InitializeManipulators) - if (newViewId.IsValid()) + if (viewEntityId.IsValid()) { - const auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(newViewId); + const auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(viewEntityId); if (entityIdLookupIt != m_entityIdManipulators.m_lookups.end()) { - m_editorCameraComponentEntityId = newViewId; + m_editorCameraComponentEntityId = viewEntityId; RegenerateManipulators(); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h index 935e80951a..6cc89b2d10 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h @@ -270,7 +270,7 @@ namespace AzToolsFramework void OnTransformChanged(const AZ::Transform& localTM, const AZ::Transform& worldTM) override; // Camera::EditorCameraNotificationBus overrides ... - void OnViewportViewEntityChanged(const AZ::EntityId& newViewId) override; + void OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) override; // EditorContextVisibilityNotificationBus overrides ... void OnEntityVisibilityChanged(bool visibility) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 3932190d8e..88febef8c2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -149,6 +149,9 @@ set(FILES Entity/SliceEditorEntityOwnershipServiceBus.h Fingerprinting/TypeFingerprinter.h Fingerprinting/TypeFingerprinter.cpp + FocusMode/FocusModeInterface.h + FocusMode/FocusModeSystemComponent.h + FocusMode/FocusModeSystemComponent.cpp Logger/TraceLogger.cpp Logger/TraceLogger.h Manipulators/AngularManipulator.cpp @@ -629,6 +632,9 @@ set(FILES Prefab/PrefabDomTypes.h Prefab/PrefabDomUtils.h Prefab/PrefabDomUtils.cpp + Prefab/PrefabFocusHandler.h + Prefab/PrefabFocusHandler.cpp + Prefab/PrefabFocusInterface.h Prefab/PrefabIdTypes.h Prefab/PrefabLoader.h Prefab/PrefabLoader.cpp @@ -721,9 +727,6 @@ set(FILES UI/Layer/LayerUiHandler.cpp UI/Prefab/LevelRootUiHandler.h UI/Prefab/LevelRootUiHandler.cpp - UI/Prefab/PrefabEditInterface.h - UI/Prefab/PrefabEditManager.h - UI/Prefab/PrefabEditManager.cpp UI/Prefab/PrefabIntegrationBus.h UI/Prefab/PrefabIntegrationManager.h UI/Prefab/PrefabIntegrationManager.cpp diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp b/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp index cffac78365..2987fc4dc2 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectBuilderWorker_linux.cpp @@ -7,14 +7,69 @@ */ #include +#include +#include + +#include +#include namespace O3DE::ProjectManager { - AZ::Outcome ProjectBuilderWorker::BuildProjectForPlatform() + AZ::Outcome ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const { - QString error = tr("Automatic building on Linux not currently supported!"); - QStringToAZTracePrint(error); - return AZ::Failure(error); + // Attempt to use the Ninja build system if it is installed (described in the o3de documentation) if possible, + // otherwise default to the the default for Linux (Unix Makefiles) + auto whichNinjaResult = ProjectUtils::ExecuteCommandResult("which", QStringList{"ninja"}, QProcessEnvironment::systemEnvironment()); + QString cmakeGenerator = (whichNinjaResult.IsSuccess()) ? "Ninja Multi-Config" : "Unix Makefiles"; + bool compileProfileOnBuild = (whichNinjaResult.IsSuccess()); + + // On Linux the default compiler is gcc. For O3DE, it is clang, so we need to specify the version of clang that is detected + // in order to get the compiler option. + auto compilerOptionResult = ProjectUtils::FindSupportedCompilerForPlatform(); + if (!compilerOptionResult.IsSuccess()) + { + return AZ::Failure(compilerOptionResult.GetError()); + } + auto clangCompilers = compilerOptionResult.GetValue().split('|'); + AZ_Assert(clangCompilers.length()==2, "Invalid clang compiler pair specification"); + + QString clangCompilerOption = clangCompilers[0]; + QString clangPPCompilerOption = clangCompilers[1]; + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + QStringList generateProjectArgs = QStringList{ProjectCMakeCommand, + "-B", ProjectBuildPathPostfix, + "-S", ".", + QString("-G%1").arg(cmakeGenerator), + QString("-DCMAKE_C_COMPILER=").append(clangCompilerOption), + QString("-DCMAKE_CXX_COMPILER=").append(clangPPCompilerOption), + QString("-DLY_3RDPARTY_PATH=").append(thirdPartyPath)}; + if (!compileProfileOnBuild) + { + generateProjectArgs.append("-DCMAKE_BUILD_TYPE=profile"); + } + return AZ::Success(generateProjectArgs); } - + + AZ::Outcome ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const + { + auto whichNinjaResult = ProjectUtils::ExecuteCommandResult("which", QStringList{"ninja"}, QProcessEnvironment::systemEnvironment()); + bool compileProfileOnBuild = (whichNinjaResult.IsSuccess()); + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher"; + + QStringList buildProjectArgs = QStringList{ProjectCMakeCommand, + "--build", ProjectBuildPathPostfix, + "--target", launcherTargetName, ProjectCMakeBuildTargetEditor}; + if (compileProfileOnBuild) + { + buildProjectArgs.append(QStringList{"--config","profile"}); + } + return AZ::Success(buildProjectArgs); + } + + AZ::Outcome ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const + { + return AZ::Success(QStringList{"kill", "-9", pidToKill}); + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp index 64d18ec605..56feb9f70a 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp @@ -6,15 +6,48 @@ */ #include +#include +#include namespace O3DE::ProjectManager { namespace ProjectUtils { - AZ::Outcome FindSupportedCompilerForPlatform() + // The list of clang C/C++ compiler command lines to validate on the host Linux system + const QStringList SupportedClangCommands = {"clang-12|clang++-12"}; + + AZ::Outcome GetCommandLineProcessEnvironment() { - // Compiler detection not supported on platform - return AZ::Success(); + return AZ::Success(QProcessEnvironment(QProcessEnvironment::systemEnvironment())); + } + + AZ::Outcome FindSupportedCompilerForPlatform() + { + // Validate that cmake is installed and is in the command line + auto whichCMakeResult = ProjectUtils::ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}, QProcessEnvironment::systemEnvironment()); + if (!whichCMakeResult.IsSuccess()) + { + return AZ::Failure(QObject::tr("CMake not found. \n\n" + "Make sure that the minimum version of CMake is installed and available from the command prompt. " + "Refer to the O3DE requirements page for more information.")); + } + + // Look for the first compatible version of clang. The list below will contain the known clang compilers that have been tested for O3DE. + for (const QString& supportClangCommand : SupportedClangCommands) + { + auto clangCompilers = supportClangCommand.split('|'); + AZ_Assert(clangCompilers.length()==2, "Invalid clang compiler pair specification"); + + auto whichClangResult = ProjectUtils::ExecuteCommandResult("which", QStringList{clangCompilers[0]}, QProcessEnvironment::systemEnvironment()); + auto whichClangPPResult = ProjectUtils::ExecuteCommandResult("which", QStringList{clangCompilers[1]}, QProcessEnvironment::systemEnvironment()); + if (whichClangResult.IsSuccess() && whichClangPPResult.IsSuccess()) + { + return AZ::Success(supportClangCommand); + } + } + return AZ::Failure(QObject::tr("Clang not found. \n\n" + "Make sure that the clang is installed and available from the command prompt. " + "Refer to the O3DE requirements page for more information.")); } } // namespace ProjectUtils diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp b/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp index 7f4c5eb5d0..ab412d84d8 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectBuilderWorker_mac.cpp @@ -7,14 +7,82 @@ */ #include +#include +#include + +#include +#include namespace O3DE::ProjectManager { - AZ::Outcome ProjectBuilderWorker::BuildProjectForPlatform() + namespace Internal { - QString error = tr("Automatic building on MacOS not currently supported!"); - QStringToAZTracePrint(error); - return AZ::Failure(error); + AZ::Outcome QueryInstalledCmakeFullPath() + { + auto environmentRequest = ProjectUtils::GetCommandLineProcessEnvironment(); + if (!environmentRequest.IsSuccess()) + { + return AZ::Failure(environmentRequest.GetError()); + } + auto currentEnvironment = environmentRequest.GetValue(); + + auto queryCmakeInstalled = ProjectUtils::ExecuteCommandResult("which", + QStringList{ProjectCMakeCommand}, + currentEnvironment); + if (!queryCmakeInstalled.IsSuccess()) + { + return AZ::Failure(QObject::tr("Unable to detect CMake on this host.")); + } + QString cmakeInstalledPath = queryCmakeInstalled.GetValue().split("\n")[0]; + return AZ::Success(cmakeInstalledPath); + } + } + + AZ::Outcome ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const + { + // For Mac, we need to resolve the full path of cmake and use that in the process request. For + // some reason, 'which' will resolve the full path, but when you just specify cmake with the same + // environment, it is unable to resolve. To work around this, we will use 'which' to resolve the + // full path and then use it as the command argument + auto cmakeInstalledPathQuery = Internal::QueryInstalledCmakeFullPath(); + if (!cmakeInstalledPathQuery.IsSuccess()) + { + return AZ::Failure(cmakeInstalledPathQuery.GetError()); + } + QString cmakeInstalledPath = cmakeInstalledPathQuery.GetValue(); + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + + return AZ::Success(QStringList{cmakeInstalledPath, + "-B", targetBuildPath, + "-S", m_projectInfo.m_path, + "-GXcode"}); + } + + AZ::Outcome ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const + { + // For Mac, we need to resolve the full path of cmake and use that in the process request. For + // some reason, 'which' will resolve the full path, but when you just specify cmake with the same + // environment, it is unable to resolve. To work around this, we will use 'which' to resolve the + // full path and then use it as the command argument + auto cmakeInstalledPathQuery = Internal::QueryInstalledCmakeFullPath(); + if (!cmakeInstalledPathQuery.IsSuccess()) + { + return AZ::Failure(cmakeInstalledPathQuery.GetError()); + } + + QString cmakeInstalledPath = cmakeInstalledPathQuery.GetValue(); + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher"; + + return AZ::Success(QStringList{cmakeInstalledPath, + "--build", targetBuildPath, + "--config", "profile", + "--target", launcherTargetName, ProjectCMakeBuildTargetEditor}); + } + + AZ::Outcome ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const + { + return AZ::Success(QStringList{"kill", "-9", pidToKill}); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp index 64d18ec605..0d150abc3b 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp @@ -7,15 +7,59 @@ #include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils { - AZ::Outcome FindSupportedCompilerForPlatform() + AZ::Outcome GetCommandLineProcessEnvironment() { - // Compiler detection not supported on platform - return AZ::Success(); + // For CMake on Mac, if its installed through home-brew, then it will be installed + // under /usr/local/bin, which may not be in the system PATH environment. + // Add that path for the command line process so that it will be able to locate + // a home-brew installed version of CMake + QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); + QString pathValue = currentEnvironment.value("PATH"); + pathValue += ":/usr/local/bin"; + currentEnvironment.insert("PATH", pathValue); + return AZ::Success(currentEnvironment); } - + + AZ::Outcome FindSupportedCompilerForPlatform() + { + QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); + QString pathValue = currentEnvironment.value("PATH"); + pathValue += ":/usr/local/bin"; + currentEnvironment.insert("PATH", pathValue); + + // Validate that we have cmake installed first + auto queryCmakeInstalled = ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}, currentEnvironment); + if (!queryCmakeInstalled.IsSuccess()) + { + return AZ::Failure(QObject::tr("Unable to detect CMake on this host.")); + } + QString cmakeInstalledPath = queryCmakeInstalled.GetValue().split("\n")[0]; + + // Query the version of the installed cmake + auto queryCmakeVersionQuery = ExecuteCommandResult(cmakeInstalledPath, QStringList{"-version"}, currentEnvironment); + if (!queryCmakeVersionQuery.IsSuccess()) + { + return AZ::Failure(QObject::tr("Unable to determine the version of CMake on this host.")); + } + AZ_TracePrintf("Project Manager", "Cmake version %s detected.", queryCmakeVersionQuery.GetValue().split("\n")[0].toUtf8().constData()); + + // Query for the version of xcodebuild (if installed) + auto queryXcodeBuildVersion = ExecuteCommandResult("xcodebuild", QStringList{"-version"}, currentEnvironment); + if (!queryCmakeInstalled.IsSuccess()) + { + return AZ::Failure(QObject::tr("Unable to detect XCodeBuilder on this host.")); + } + QString xcodeBuilderVersionNumber = queryXcodeBuildVersion.GetValue().split("\n")[0]; + AZ_TracePrintf("Project Manager", "XcodeBuilder version %s detected.", xcodeBuilderVersionNumber.toUtf8().constData()); + + + return AZ::Success(xcodeBuilderVersionNumber); + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp b/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp index 8856e2312a..075c6de774 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp @@ -8,189 +8,37 @@ #include #include -#include #include -#include -#include -#include -#include -#include +#include namespace O3DE::ProjectManager { - AZ::Outcome ProjectBuilderWorker::BuildProjectForPlatform() + AZ::Outcome ProjectBuilderWorker::ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const { - // Check if we are trying to cancel task - if (QThread::currentThread()->isInterruptionRequested()) - { - QStringToAZTracePrint(BuildCancelled); - return AZ::Failure(BuildCancelled); - } + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); - QFile logFile(GetLogFilePath()); - if (!logFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) - { - QString error = tr("Failed to open log file."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } + return AZ::Success(QStringList{ ProjectCMakeCommand, + "-B", targetBuildPath, + "-S", m_projectInfo.m_path, + QString("-DLY_3RDPARTY_PATH=").append(thirdPartyPath), + "-DLY_UNITY_BUILD=ON" } ); + } - EngineInfo engineInfo; + AZ::Outcome ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const + { + QString targetBuildPath = QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix); + QString launcherTargetName = m_projectInfo.m_projectName + ".GameLauncher"; - AZ::Outcome engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); - if (engineInfoResult.IsSuccess()) - { - engineInfo = engineInfoResult.GetValue(); - } - else - { - QString error = tr("Failed to get engine info."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } + return AZ::Success(QStringList{ ProjectCMakeCommand, + "--build", targetBuildPath, + "--config", "profile", + "--target", launcherTargetName, ProjectCMakeBuildTargetEditor }); + } - QTextStream logStream(&logFile); - if (QThread::currentThread()->isInterruptionRequested()) - { - logFile.close(); - QStringToAZTracePrint(BuildCancelled); - return AZ::Failure(BuildCancelled); - } - - // Show some kind of progress with very approximate estimates - UpdateProgress(++m_progressEstimate); - - QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); - // Append cmake path to PATH incase it is missing - QDir cmakePath(engineInfo.m_path); - cmakePath.cd("cmake/runtime/bin"); - QString pathValue = currentEnvironment.value("PATH"); - pathValue += ";" + cmakePath.path(); - currentEnvironment.insert("PATH", pathValue); - - m_configProjectProcess = new QProcess(this); - m_configProjectProcess->setProcessChannelMode(QProcess::MergedChannels); - m_configProjectProcess->setWorkingDirectory(m_projectInfo.m_path); - m_configProjectProcess->setProcessEnvironment(currentEnvironment); - - m_configProjectProcess->start( - "cmake", - QStringList - { - "-B", - QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), - "-S", - m_projectInfo.m_path, - "-G", - "Visual Studio 16", - "-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath, - "-DLY_UNITY_BUILD=1" - }); - - if (!m_configProjectProcess->waitForStarted()) - { - QString error = tr("Configuring project failed to start."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } - bool containsGeneratingDone = false; - while (m_configProjectProcess->waitForReadyRead(MaxBuildTimeMSecs)) - { - QString configOutput = m_configProjectProcess->readAllStandardOutput(); - - if (configOutput.contains("Generating done")) - { - containsGeneratingDone = true; - } - - logStream << configOutput; - logStream.flush(); - - UpdateProgress(qMin(++m_progressEstimate, 19)); - - if (QThread::currentThread()->isInterruptionRequested()) - { - logFile.close(); - m_configProjectProcess->close(); - QStringToAZTracePrint(BuildCancelled); - return AZ::Failure(BuildCancelled); - } - } - - if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit - || m_configProjectProcess->exitCode() != 0 - || !containsGeneratingDone) - { - QString error = tr("Configuring project failed. See log for details."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } - - UpdateProgress(++m_progressEstimate); - - m_buildProjectProcess = new QProcess(this); - m_buildProjectProcess->setProcessChannelMode(QProcess::MergedChannels); - m_buildProjectProcess->setWorkingDirectory(m_projectInfo.m_path); - m_buildProjectProcess->setProcessEnvironment(currentEnvironment); - - m_buildProjectProcess->start( - "cmake", - QStringList - { - "--build", - QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), - "--target", - m_projectInfo.m_projectName + ".GameLauncher", - "Editor", - "--config", - "profile" - }); - - if (!m_buildProjectProcess->waitForStarted()) - { - QString error = tr("Building project failed to start."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } - - // There are a lot of steps when building so estimate around 800 more steps ((100 - 20) * 10) remaining - m_progressEstimate = 200; - while (m_buildProjectProcess->waitForReadyRead(MaxBuildTimeMSecs)) - { - logStream << m_buildProjectProcess->readAllStandardOutput(); - logStream.flush(); - - // Show 1% progress for every 10 steps completed - UpdateProgress(qMin(++m_progressEstimate / 10, 99)); - - if (QThread::currentThread()->isInterruptionRequested()) - { - // QProcess is unable to kill its child processes so we need to ask the operating system to do that for us - QProcess killBuildProcess; - killBuildProcess.setProcessChannelMode(QProcess::MergedChannels); - killBuildProcess.start( - "cmd.exe", QStringList{ "/C", "taskkill", "/pid", QString::number(m_buildProjectProcess->processId()), "/f", "/t" }); - killBuildProcess.waitForFinished(); - - logStream << "Killing Project Build."; - logStream << killBuildProcess.readAllStandardOutput(); - m_buildProjectProcess->kill(); - logFile.close(); - QStringToAZTracePrint(BuildCancelled); - return AZ::Failure(BuildCancelled); - } - } - - if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit - || m_configProjectProcess->exitCode() != 0) - { - QString error = tr("Building project failed. See log for details."); - QStringToAZTracePrint(error); - return AZ::Failure(error); - } - - return AZ::Success(); + AZ::Outcome ProjectBuilderWorker::ConstructKillProcessCommandArguments(const QString& pidToKill) const + { + return AZ::Success(QStringList { "cmd.exe", "/C", "taskkill", "/pid", pidToKill, "/f", "/t" } ); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp index 7ca51fb9c5..d012ca8921 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp @@ -7,6 +7,8 @@ #include +#include + #include #include #include @@ -16,8 +18,44 @@ namespace O3DE::ProjectManager { namespace ProjectUtils { - AZ::Outcome FindSupportedCompilerForPlatform() + AZ::Outcome GetCommandLineProcessEnvironment() { + // Use the engine path to insert a path for cmake + auto engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); + if (!engineInfoResult.IsSuccess()) + { + return AZ::Failure(QObject::tr("Failed to get engine info")); + } + auto engineInfo = engineInfoResult.GetValue(); + + QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); + + // Append cmake path to PATH incase it is missing + QDir cmakePath(engineInfo.m_path); + cmakePath.cd("cmake/runtime/bin"); + QString pathValue = currentEnvironment.value("PATH"); + pathValue += ";" + cmakePath.path(); + currentEnvironment.insert("PATH", pathValue); + return AZ::Success(currentEnvironment); + } + + AZ::Outcome FindSupportedCompilerForPlatform() + { + // Validate that cmake is installed + auto cmakeProcessEnvResult = GetCommandLineProcessEnvironment(); + if (!cmakeProcessEnvResult.IsSuccess()) + { + return AZ::Failure(cmakeProcessEnvResult.GetError()); + } + auto cmakeVersionQueryResult = ExecuteCommandResult("cmake", QStringList{"--version"}, cmakeProcessEnvResult.GetValue()); + if (!cmakeVersionQueryResult.IsSuccess()) + { + return AZ::Failure(QObject::tr("CMake not found. \n\n" + "Make sure that the minimum version of CMake is installed and available from the command prompt. " + "Refer to the O3DE requirements for more information.")); + } + + // Validate that the minimal version of visual studio is installed QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); QString programFilesPath = environment.value("ProgramFiles(x86)"); QString vsWherePath = QDir(programFilesPath).filePath("Microsoft Visual Studio/Installer/vswhere.exe"); @@ -25,27 +63,31 @@ namespace O3DE::ProjectManager QFileInfo vsWhereFile(vsWherePath); if (vsWhereFile.exists() && vsWhereFile.isFile()) { - QProcess vsWhereProcess; - vsWhereProcess.setProcessChannelMode(QProcess::MergedChannels); + QStringList vsWhereBaseArguments = QStringList{"-version", + "16.9.2", + "-latest", + "-requires", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64"}; - vsWhereProcess.start( - vsWherePath, - QStringList{ - "-version", - "16.9.2", - "-latest", - "-requires", - "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "-property", - "isComplete" - }); + QProcess vsWhereIsCompleteProcess; + vsWhereIsCompleteProcess.setProcessChannelMode(QProcess::MergedChannels); - if (vsWhereProcess.waitForStarted() && vsWhereProcess.waitForFinished()) + vsWhereIsCompleteProcess.start(vsWherePath, vsWhereBaseArguments + QStringList{ "-property", "isComplete" }); + + if (vsWhereIsCompleteProcess.waitForStarted() && vsWhereIsCompleteProcess.waitForFinished()) { - QString vsWhereOutput(vsWhereProcess.readAllStandardOutput()); - if (vsWhereOutput.startsWith("1")) + QString vsWhereIsCompleteOutput(vsWhereIsCompleteProcess.readAllStandardOutput()); + if (vsWhereIsCompleteOutput.startsWith("1")) { - return AZ::Success(); + QProcess vsWhereCompilerVersionProcess; + vsWhereCompilerVersionProcess.setProcessChannelMode(QProcess::MergedChannels); + vsWhereCompilerVersionProcess.start(vsWherePath, vsWhereBaseArguments + QStringList{"-property", "catalog_productDisplayVersion"}); + + if (vsWhereCompilerVersionProcess.waitForStarted() && vsWhereCompilerVersionProcess.waitForFinished()) + { + QString vsWhereCompilerVersionOutput(vsWhereCompilerVersionProcess.readAllStandardOutput()); + return AZ::Success(vsWhereCompilerVersionOutput); + } } } } diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp index 2fe1c6db15..c6a6b20a1d 100644 --- a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp @@ -8,8 +8,15 @@ #include #include +#include +#include #include +#include +#include +#include +#include +#include //#define MOCK_BUILD_PROJECT true @@ -67,4 +74,176 @@ namespace O3DE::ProjectManager { AZ_TracePrintf("Project Manager", error.toStdString().c_str()); } + + AZ::Outcome ProjectBuilderWorker::BuildProjectForPlatform() + { + // Check if we are trying to cancel task + if (QThread::currentThread()->isInterruptionRequested()) + { + QStringToAZTracePrint(BuildCancelled); + return AZ::Failure(BuildCancelled); + } + + QFile logFile(GetLogFilePath()); + if (!logFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) + { + QString error = tr("Failed to open log file."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + EngineInfo engineInfo; + + AZ::Outcome engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo(); + if (engineInfoResult.IsSuccess()) + { + engineInfo = engineInfoResult.GetValue(); + } + else + { + QString error = tr("Failed to get engine info."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + QTextStream logStream(&logFile); + if (QThread::currentThread()->isInterruptionRequested()) + { + logFile.close(); + QStringToAZTracePrint(BuildCancelled); + return AZ::Failure(BuildCancelled); + } + + // Show some kind of progress with very approximate estimates + UpdateProgress(++m_progressEstimate); + + auto currentEnvironmentRequest = ProjectUtils::GetCommandLineProcessEnvironment(); + if (!currentEnvironmentRequest.IsSuccess()) + { + QStringToAZTracePrint(currentEnvironmentRequest.GetError()); + return AZ::Failure(currentEnvironmentRequest.GetError()); + } + QProcessEnvironment currentEnvironment = currentEnvironmentRequest.GetValue(); + + m_configProjectProcess = new QProcess(this); + m_configProjectProcess->setProcessChannelMode(QProcess::MergedChannels); + m_configProjectProcess->setWorkingDirectory(m_projectInfo.m_path); + m_configProjectProcess->setProcessEnvironment(currentEnvironment); + + auto cmakeGenerateArgumentsResult = ConstructCmakeGenerateProjectArguments(engineInfo.m_thirdPartyPath); + if (!cmakeGenerateArgumentsResult.IsSuccess()) + { + QStringToAZTracePrint(cmakeGenerateArgumentsResult.GetError()); + return AZ::Failure(cmakeGenerateArgumentsResult.GetError()); + } + auto cmakeGenerateArguments = cmakeGenerateArgumentsResult.GetValue(); + m_configProjectProcess->start(cmakeGenerateArguments.front(), cmakeGenerateArguments.mid(1)); + if (!m_configProjectProcess->waitForStarted()) + { + QString error = tr("Configuring project failed to start."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + bool containsGeneratingDone = false; + while (m_configProjectProcess->waitForReadyRead(MaxBuildTimeMSecs)) + { + QString configOutput = m_configProjectProcess->readAllStandardOutput(); + + if (configOutput.contains("Generating done")) + { + containsGeneratingDone = true; + } + + logStream << configOutput; + logStream.flush(); + + UpdateProgress(qMin(++m_progressEstimate, 19)); + + if (QThread::currentThread()->isInterruptionRequested()) + { + logFile.close(); + m_configProjectProcess->close(); + QStringToAZTracePrint(BuildCancelled); + return AZ::Failure(BuildCancelled); + } + } + + if (m_configProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit || m_configProjectProcess->exitCode() != 0 || + !containsGeneratingDone) + { + QString error = tr("Configuring project failed. See log for details."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + UpdateProgress(++m_progressEstimate); + + m_buildProjectProcess = new QProcess(this); + m_buildProjectProcess->setProcessChannelMode(QProcess::MergedChannels); + m_buildProjectProcess->setWorkingDirectory(m_projectInfo.m_path); + m_buildProjectProcess->setProcessEnvironment(currentEnvironment); + + auto cmakeBuildArgumentsResult = ConstructCmakeBuildCommandArguments(); + if (!cmakeBuildArgumentsResult.IsSuccess()) + { + QStringToAZTracePrint(cmakeBuildArgumentsResult.GetError()); + return AZ::Failure(cmakeBuildArgumentsResult.GetError()); + } + auto cmakeBuildArguments = cmakeBuildArgumentsResult.GetValue(); + + m_buildProjectProcess->start(cmakeBuildArguments.front(), cmakeBuildArguments.mid(1)); + if (!m_buildProjectProcess->waitForStarted()) + { + QString error = tr("Building project failed to start."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + // There are a lot of steps when building so estimate around 800 more steps ((100 - 20) * 10) remaining + m_progressEstimate = 200; + while (m_buildProjectProcess->waitForReadyRead(MaxBuildTimeMSecs)) + { + logStream << m_buildProjectProcess->readAllStandardOutput(); + logStream.flush(); + + // Show 1% progress for every 10 steps completed + UpdateProgress(qMin(++m_progressEstimate / 10, 99)); + + if (QThread::currentThread()->isInterruptionRequested()) + { + // QProcess is unable to kill its child processes so we need to ask the operating system to do that for us + auto killProcessArgumentsResult = ConstructKillProcessCommandArguments(QString::number(m_buildProjectProcess->processId())); + if (!killProcessArgumentsResult.IsSuccess()) + { + return AZ::Failure(killProcessArgumentsResult.GetError()); + } + auto killProcessArguments = killProcessArgumentsResult.GetValue(); + + + QProcess killBuildProcess; + + + killBuildProcess.setProcessChannelMode(QProcess::MergedChannels); + killBuildProcess.start(killProcessArguments.front(), killProcessArguments.mid(1)); + killBuildProcess.waitForFinished(); + + logStream << "Killing Project Build."; + logStream << killBuildProcess.readAllStandardOutput(); + m_buildProjectProcess->kill(); + logFile.close(); + QStringToAZTracePrint(BuildCancelled); + return AZ::Failure(BuildCancelled); + } + } + + if (m_buildProjectProcess->exitStatus() != QProcess::ExitStatus::NormalExit || m_buildProjectProcess->exitCode() != 0) + { + QString error = tr("Building project failed. See log for details."); + QStringToAZTracePrint(error); + return AZ::Failure(error); + } + + return AZ::Success(); + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h index f42c87f47b..94c4a6bd52 100644 --- a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h +++ b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h @@ -12,6 +12,7 @@ #include #include +#include #endif QT_FORWARD_DECLARE_CLASS(QProcess) @@ -44,6 +45,12 @@ namespace O3DE::ProjectManager AZ::Outcome BuildProjectForPlatform(); void QStringToAZTracePrint(const QString& error); + // Command line argument builders + AZ::Outcome ConstructCmakeGenerateProjectArguments(const QString& thirdPartyPath) const; + AZ::Outcome ConstructCmakeBuildCommandArguments() const; + AZ::Outcome ConstructKillProcessCommandArguments(const QString& pidToKill) const; + + QProcess* m_configProjectProcess = nullptr; QProcess* m_buildProjectProcess = nullptr; ProjectInfo m_projectInfo; diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h b/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h index 8ab5128741..264515652f 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h +++ b/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h @@ -14,6 +14,7 @@ namespace O3DE::ProjectManager inline constexpr static int ProjectPreviewImageWidth = 210; inline constexpr static int ProjectPreviewImageHeight = 280; inline constexpr static int ProjectTemplateImageWidth = 92; + inline constexpr static int ProjectCommandLineTimeoutSeconds = 30; static const QString ProjectBuildDirectoryName = "build"; extern const QString ProjectBuildPathPostfix; @@ -21,4 +22,8 @@ namespace O3DE::ProjectManager static const QString ProjectBuildErrorLogName = "CMakeProjectBuildError.log"; static const QString ProjectCacheDirectoryName = "Cache"; static const QString ProjectPreviewImagePath = "preview.png"; + + static const QString ProjectCMakeCommand = "cmake"; + static const QString ProjectCMakeBuildTargetEditor = "Editor"; + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp index 84d731832e..81eb70391d 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp @@ -22,6 +22,8 @@ #include #include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils @@ -507,5 +509,33 @@ namespace O3DE::ProjectManager return ProjectManagerScreen::Invalid; } + + AZ::Outcome ExecuteCommandResult( + const QString& cmd, + const QStringList& arguments, + const QProcessEnvironment& processEnv, + int commandTimeoutSeconds /*= ProjectCommandLineTimeoutSeconds*/) + { + QProcess execProcess; + execProcess.setProcessEnvironment(processEnv); + execProcess.setProcessChannelMode(QProcess::MergedChannels); + execProcess.start(cmd, arguments); + if (!execProcess.waitForStarted()) + { + return AZ::Failure(QObject::tr("Unable to start process for command '%1'").arg(cmd)); + } + + if (!execProcess.waitForFinished(commandTimeoutSeconds * 1000 /* Milliseconds per second */)) + { + return AZ::Failure(QObject::tr("Process for command '%1' timed out at %2 seconds").arg(cmd).arg(commandTimeoutSeconds)); + } + int resultCode = execProcess.exitCode(); + if (resultCode != 0) + { + return AZ::Failure(QObject::tr("Process for command '%1' failed (result code %2").arg(cmd).arg(resultCode)); + } + QString resultOutput = execProcess.readAllStandardOutput(); + return AZ::Success(resultOutput); + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.h b/Code/Tools/ProjectManager/Source/ProjectUtils.h index f1050531d4..0866b57923 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.h +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.h @@ -9,8 +9,11 @@ #include #include +#include #include +#include + #include namespace O3DE::ProjectManager @@ -28,8 +31,17 @@ namespace O3DE::ProjectManager bool ReplaceProjectFile(const QString& origFile, const QString& newFile, QWidget* parent = nullptr, bool interactive = true); bool FindSupportedCompiler(QWidget* parent = nullptr); - AZ::Outcome FindSupportedCompilerForPlatform(); + AZ::Outcome FindSupportedCompilerForPlatform(); ProjectManagerScreen GetProjectManagerScreen(const QString& screen); + + AZ::Outcome ExecuteCommandResult( + const QString& cmd, + const QStringList& arguments, + const QProcessEnvironment& processEnv, + int commandTimeoutSeconds = ProjectCommandLineTimeoutSeconds); + + AZ::Outcome GetCommandLineProcessEnvironment(); + } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 6f8ff9abce..c92ad53cd4 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -339,7 +339,7 @@ namespace O3DE::ProjectManager { for (auto engine : allEngines) { - AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"])); + AZ::IO::FixedMaxPath enginePath(Py_To_String(engine)); if (enginePath.Compare(m_enginePath) == 0) { return; diff --git a/Gems/AWSClientAuth/gem.json b/Gems/AWSClientAuth/gem.json index 42996e7f4f..75c07d025b 100644 --- a/Gems/AWSClientAuth/gem.json +++ b/Gems/AWSClientAuth/gem.json @@ -5,9 +5,19 @@ "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "AWS Client Auth provides client authentication and AWS authorization solution.", - "canonical_tags": ["Gem"], - "user_tags": ["AWS", "Network", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "AWS", + "Network", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/", + "dependencies": [ + "AWSCore", + "HttpRequestor" + ] } diff --git a/Gems/AWSCore/gem.json b/Gems/AWSCore/gem.json index 4c7889ace4..1bb9da9192 100644 --- a/Gems/AWSCore/gem.json +++ b/Gems/AWSCore/gem.json @@ -5,9 +5,16 @@ "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS Core Gem provides basic shared AWS functionality such as AWS SDK initialization and client configuration, and is automatically added when selecting any AWS feature Gem.", - "canonical_tags": ["Gem"], - "user_tags": ["AWS", "Network", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "AWS", + "Network", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-core/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-core/", + "dependencies": [] } diff --git a/Gems/AWSGameLift/gem.json b/Gems/AWSGameLift/gem.json index 7711495ee7..a0d7f62cd1 100644 --- a/Gems/AWSGameLift/gem.json +++ b/Gems/AWSGameLift/gem.json @@ -5,9 +5,18 @@ "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS GameLift Gem provides a framework to extend O3DE networking layer to work with GameLift resources via GameLift server and client SDK.", - "canonical_tags": ["Gem"], - "user_tags": ["AWS", "Framework", "Network"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "AWS", + "Framework", + "Network" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/", + "dependencies": [ + "AWSCore" + ] } diff --git a/Gems/AWSMetrics/gem.json b/Gems/AWSMetrics/gem.json index 5faeb66787..df16890012 100644 --- a/Gems/AWSMetrics/gem.json +++ b/Gems/AWSMetrics/gem.json @@ -5,9 +5,18 @@ "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS Metrics Gem provides a solution for AWS metrics submission and analytics.", - "canonical_tags": ["Gem"], - "user_tags": ["AWS", "Network", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "AWS", + "Network", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/", + "dependencies": [ + "AWSCore" + ] } diff --git a/Gems/Achievements/gem.json b/Gems/Achievements/gem.json index ca2bc7f3e9..bd643f471a 100644 --- a/Gems/Achievements/gem.json +++ b/Gems/Achievements/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Achievements Gem provides a target platform agnostic interface for retrieving achievement details and unlocking achievements.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Achievements"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Achievements" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/achievements/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/achievements/", + "dependencies": [] } diff --git a/Gems/AssetMemoryAnalyzer/gem.json b/Gems/AssetMemoryAnalyzer/gem.json index 544e0f7948..902102fa27 100644 --- a/Gems/AssetMemoryAnalyzer/gem.json +++ b/Gems/AssetMemoryAnalyzer/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Asset Memory Analyzer Gem provides tools to profile asset memory usage in Open 3D Engine through ImGUI (Immediate Mode Graphical User Interface).", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Utility", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Utility", + "Tools" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/asset-memory-analyzer/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/asset-memory-analyzer/", + "dependencies": [ + "ImGui" + ] } diff --git a/Gems/AssetValidation/gem.json b/Gems/AssetValidation/gem.json index 83e37ddaf5..1e57f60dc4 100644 --- a/Gems/AssetValidation/gem.json +++ b/Gems/AssetValidation/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Asset Validation Gem provides seed-related commands to ensure assets have valid seeds for asset bundling.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Utility", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Utility", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/asset-validation/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/asset-validation/", + "dependencies": [] } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/PixelOperation.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/PixelOperation.cpp index 3e897078fa..fa4baeefc0 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/PixelOperation.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/PixelOperation.cpp @@ -248,8 +248,8 @@ namespace ImageProcessingAtom { const uint8* data = buf; r = U8ToF32(data[0]); - g = 0.f; - b = 0.f; + g = r; + b = r; a = 1.f; } @@ -333,8 +333,8 @@ namespace ImageProcessingAtom { const uint16* data = (uint16*)(buf); r = U16ToF32(data[0]); - g = 0.f; - b = 0.f; + g = r; + b = r; a = 1.f; } @@ -418,8 +418,8 @@ namespace ImageProcessingAtom { const float* data = (float*)(buf); r = data[0]; - g = 0.f; - b = 0.f; + g = r; + b = r; a = 1.f; } @@ -485,8 +485,8 @@ namespace ImageProcessingAtom { const SHalf* data = (SHalf*)(buf); r = data[0]; - g = 0.f; - b = 0.f; + g = r; + b = r; a = 1.f; } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset index 3a5122f18e..e0d043cf20 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset @@ -39,7 +39,7 @@ "_bc", "_diffuse" ], - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "DiscardAlpha": true, "IsPowerOf2": true, "MipMapSetting": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset index 692ef99b1c..fda5a9cc52 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset @@ -36,7 +36,7 @@ "_bc", "_diffuse" ], - "PixelFormat": "ETC2a1", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset index 4ebe773f0e..8c196530e9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset @@ -36,7 +36,7 @@ "_bc", "_diffuse" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset index 6049ef5bd4..346f0f8cac 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithOpacity.preset @@ -36,7 +36,7 @@ "_bc", "_diffuse" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset index 56dec20f3e..638e31f838 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset @@ -28,7 +28,7 @@ "_amb", "_ambientocclusion" ], - "PixelFormat": "EAC_R11" + "PixelFormat": "ASTC_4x4" }, "ios": { "UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}", @@ -41,7 +41,7 @@ "_amb", "_ambientocclusion" ], - "PixelFormat": "EAC_R11" + "PixelFormat": "ASTC_4x4" }, "mac": { "UUID": "{02ED0ECE-B198-49D9-85BC-CEBA6C28546C}", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset index 2280a06302..8d1105cdfc 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/CloudShadows.preset @@ -15,14 +15,14 @@ "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", "Name": "CloudShadows", "DestColor": "Linear", - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true }, "ios": { "UUID": "{884B5F7C-44AC-4E9E-8B8A-559D098BE2C7}", "Name": "CloudShadows", "DestColor": "Linear", - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true }, "mac": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset index f1e43e74b1..628cd673e9 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset @@ -24,13 +24,13 @@ "FileMasks": [ "_decal" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" }, // Decal Texture Arrays need all mips available immediately for packing. - "NumberResidentMips": 255 + "NumberResidentMips": 255 }, "ios": { "UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset index 991692c5cc..5bfea9a376 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Detail_MergedAlbedoNormalsSmoothness.preset @@ -26,7 +26,7 @@ "FileMasks": [ "_detail" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset index 520e4ae193..8f21ad005b 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset @@ -45,7 +45,7 @@ "_ht", "_h" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "DiscardAlpha": true, "IsPowerOf2": true, "SizeReduceLevel": 3, @@ -70,7 +70,7 @@ "_ht", "_h" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "DiscardAlpha": true, "IsPowerOf2": true, "MipMapSetting": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset index ffb16482fd..8f6c846e82 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset @@ -29,7 +29,7 @@ "_em", "_emit" ], - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "DiscardAlpha": true }, "ios": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset index f06682be42..c71ada1269 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset @@ -26,7 +26,7 @@ "FileMasks": [ "_mask" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" @@ -40,7 +40,7 @@ "FileMasks": [ "_mask" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset index 9f4b5bf68d..d277785151 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LensOptics.preset @@ -12,7 +12,7 @@ "android": { "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", "Name": "LensOptics", - "PixelFormat": "ETC2" + "PixelFormat": "ASTC_4x4" }, "ios": { "UUID": "{3000A993-0A04-4E08-A813-DFB1A47A0980}", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset index ede264a78e..4de95427d6 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/LightProjector.preset @@ -18,7 +18,7 @@ "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", "Name": "LightProjector", "DestColor": "Linear", - "PixelFormat": "EAC_RG11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" @@ -28,7 +28,7 @@ "UUID": "{1DFEF41A-D97F-40FB-99D3-C142A3E5225E}", "Name": "LightProjector", "DestColor": "Linear", - "PixelFormat": "EAC_RG11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset index 9370de063d..79dda1977e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Minimap.preset @@ -19,7 +19,7 @@ "UUID": "{0D2F4C31-A665-4862-9C63-9E49A58E9A37}", "Name": "Minimap", "SuppressEngineReduce": true, - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "SizeReduceLevel": 1, "MipMapSetting": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset index 459cd5b1fb..b02227b454 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/MuzzleFlash.preset @@ -18,7 +18,7 @@ "UUID": "{8BCC23A5-D08E-458E-B0B3-087C65FA1D31}", "Name": "MuzzleFlash", "SuppressEngineReduce": true, - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset index bbd7fd5db9..8b66b30f28 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset @@ -44,7 +44,7 @@ "_msk", "_blend" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" @@ -67,7 +67,7 @@ "_msk", "_blend" ], - "PixelFormat": "EAC_R11", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset index 1844e0186e..e9ea34060b 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset @@ -57,7 +57,7 @@ "_roughness", "_rough" ], - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset index e51cc7122b..e868a44096 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/ReflectanceWithSmoothness_Legacy.preset @@ -22,7 +22,7 @@ "FileMasks": [ "_spec" ], - "PixelFormat": "ETC2a", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset index 07cc39c955..e52b616f48 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance_Linear.preset @@ -26,7 +26,7 @@ "_spec", "_refl" ], - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset index 46a32ce5d4..191425bb92 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image.preset @@ -19,7 +19,7 @@ "SourceColor": "Linear", "DestColor": "Linear", "SuppressEngineReduce": true, - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true }, "ios": { @@ -28,7 +28,7 @@ "SourceColor": "Linear", "DestColor": "Linear", "SuppressEngineReduce": true, - "PixelFormat": "PVRTC4", + "PixelFormat": "ASTC_4x4", "IsPowerOf2": true }, "mac": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset index 0ba70d2ca3..9b1a9c7c45 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/SF_Image_nonpower2.preset @@ -18,7 +18,7 @@ "SourceColor": "Linear", "DestColor": "Linear", "SuppressEngineReduce": true, - "PixelFormat": "ETC2" + "PixelFormat": "ASTC_4x4" }, "ios": { "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", @@ -26,7 +26,7 @@ "SourceColor": "Linear", "DestColor": "Linear", "SuppressEngineReduce": true, - "PixelFormat": "PVRTC4" + "PixelFormat": "ASTC_4x4" }, "mac": { "UUID": "{C456B8AB-C360-4822-BCDD-225252D0E697}", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset index 84a70935f1..8b12a08465 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo.preset @@ -21,7 +21,7 @@ "Name": "Terrain_Albedo", "SourceColor": "Linear", "DestColor": "Linear", - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "HighPassMip": 5, "MipMapSetting": { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset index 1d83737ef9..d24531858f 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/Terrain_Albedo_HighPassed.preset @@ -20,7 +20,7 @@ "Name": "Terrain_Albedo_HighPassed", "SourceColor": "Linear", "DestColor": "Linear", - "PixelFormat": "ETC2", + "PixelFormat": "ASTC_6x6", "IsPowerOf2": true, "MipMapSetting": { "MipGenType": "Box" diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset b/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset index 6f70e8f14f..13334de700 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset +++ b/Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset @@ -17,7 +17,7 @@ "UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}", "Name": "UserInterface_Compressed", "SuppressEngineReduce": true, - "PixelFormat": "ETC2" + "PixelFormat": "ASTC_6x6" }, "ios": { "UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/gem.json b/Gems/Atom/Asset/ImageProcessingAtom/gem.json index 45d96f7168..1424841256 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/gem.json +++ b/Gems/Atom/Asset/ImageProcessingAtom/gem.json @@ -8,7 +8,11 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI", + "Atom_RHI", + "Atom" + ] } diff --git a/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Android/Vulkan/AzslcHeader.azsli b/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Android/Vulkan/AzslcHeader.azsli index 54c9430f87..0cab4d7a38 100644 --- a/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Android/Vulkan/AzslcHeader.azsli +++ b/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Android/Vulkan/AzslcHeader.azsli @@ -16,6 +16,11 @@ static const float4 s_AzslDebugColor = float4(165.0 / 255.0, 30.0 / 255.0, 36.0 / 255.0, 1); -// Uniform limitation need to be taken into consideration for mobile devices +// Uniform limitation need to be taken into consideration for mobile devices. This would help alleviate device lost errors if the constant buffer +// size overshoots the device's memory constraints. An example is a large number of constant buffers associated with mesh instances that would result in device lost, +// but otherwise OK on PC. We can separate out the number of instances for the different platforms with this define. // [ATOM-14949] -#define AZ_TRAIT_CONSTANT_BUFFER_LIMITATIONS 1 \ No newline at end of file +#define AZ_TRAIT_CONSTANT_BUFFER_LIMITATIONS 1 + +// Different constant buffer alignment on platforms +#define AZ_TRAIT_CONSTANT_BUFFER_ALIGNMENT 16 diff --git a/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Windows/Vulkan/AzslcHeader.azsli b/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Windows/Vulkan/AzslcHeader.azsli index b620c0d696..58395ef6a4 100644 --- a/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Windows/Vulkan/AzslcHeader.azsli +++ b/Gems/Atom/Asset/Shader/Code/AZSL/Platform/Windows/Vulkan/AzslcHeader.azsli @@ -12,3 +12,7 @@ */ static const float4 s_AzslDebugColor = float4(165.0 / 255.0, 30.0 / 255.0, 36.0 / 255.0, 1); + + +// Different constant buffer alignment on platforms +#define AZ_TRAIT_CONSTANT_BUFFER_ALIGNMENT 128 \ No newline at end of file diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp index eb91d9e866..e9ac18a432 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderBuilderUtility.cpp @@ -37,6 +37,7 @@ #include #include "ShaderPlatformInterfaceRequest.h" +#include "ShaderBuilder_Traits_Platform.h" #include "AtomShaderConfig.h" #include "SrgLayoutUtility.h" @@ -456,8 +457,9 @@ namespace AZ const uint32_t rhiUniqueIndex, const AZStd::string& platformIdentifier, const AZStd::string& shaderJsonPath, const uint32_t supervariantIndex, RPI::ShaderAssetSubId shaderAssetSubId) { - // platform id from identifier - AzFramework::PlatformId platformId = AzFramework::PlatformId::PC; + // Define a fallback platform ID based on the current host platform + AzFramework::PlatformId platformId = AZ_TRAIT_ATOM_FALLBACK_ASSET_HOST_PLATFORM; + if (platformIdentifier == "pc") { platformId = AzFramework::PlatformId::PC; @@ -478,6 +480,10 @@ namespace AZ { platformId = AzFramework::PlatformId::IOS; } + else if (platformIdentifier == "server") + { + platformId = AzFramework::PlatformId::SERVER; + } uint32_t assetSubId = RPI::ShaderAsset::MakeProductAssetSubId(rhiUniqueIndex, supervariantIndex, aznumeric_cast(shaderAssetSubId)); auto assetIdOutcome = RPI::AssetUtils::MakeAssetId(shaderJsonPath, assetSubId); diff --git a/Gems/Atom/Asset/Shader/Code/Source/Platform/Android/ShaderBuilder_Traits_Android.h b/Gems/Atom/Asset/Shader/Code/Source/Platform/Android/ShaderBuilder_Traits_Android.h index f8d93059f3..86afcd201c 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Platform/Android/ShaderBuilder_Traits_Android.h +++ b/Gems/Atom/Asset/Shader/Code/Source/Platform/Android/ShaderBuilder_Traits_Android.h @@ -8,3 +8,4 @@ #pragma once #define AZ_TRAIT_ATOM_SHADERBUILDER_AZSLC UNUSED_TRAIT +#define AZ_TRAIT_ATOM_FALLBACK_ASSET_HOST_PLATFORM UNUSED_TRAIT diff --git a/Gems/Atom/Asset/Shader/Code/Source/Platform/Linux/ShaderBuilder_Traits_Linux.h b/Gems/Atom/Asset/Shader/Code/Source/Platform/Linux/ShaderBuilder_Traits_Linux.h index efa5a3e9ea..54a0a1fd5d 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Platform/Linux/ShaderBuilder_Traits_Linux.h +++ b/Gems/Atom/Asset/Shader/Code/Source/Platform/Linux/ShaderBuilder_Traits_Linux.h @@ -8,4 +8,4 @@ #pragma once #define AZ_TRAIT_ATOM_SHADERBUILDER_AZSLC "azslc" - +#define AZ_TRAIT_ATOM_FALLBACK_ASSET_HOST_PLATFORM AzFramework::PlatformId::LINUX_ID diff --git a/Gems/Atom/Asset/Shader/Code/Source/Platform/Mac/ShaderBuilder_Traits_Mac.h b/Gems/Atom/Asset/Shader/Code/Source/Platform/Mac/ShaderBuilder_Traits_Mac.h index d47967a559..7b93324711 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Platform/Mac/ShaderBuilder_Traits_Mac.h +++ b/Gems/Atom/Asset/Shader/Code/Source/Platform/Mac/ShaderBuilder_Traits_Mac.h @@ -8,3 +8,4 @@ #pragma once #define AZ_TRAIT_ATOM_SHADERBUILDER_AZSLC "azslc" +#define AZ_TRAIT_ATOM_FALLBACK_ASSET_HOST_PLATFORM AzFramework::PlatformId::MAC_ID diff --git a/Gems/Atom/Asset/Shader/Code/Source/Platform/Windows/ShaderBuilder_Traits_Windows.h b/Gems/Atom/Asset/Shader/Code/Source/Platform/Windows/ShaderBuilder_Traits_Windows.h index 3645897fa2..d6dd19fbfb 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Platform/Windows/ShaderBuilder_Traits_Windows.h +++ b/Gems/Atom/Asset/Shader/Code/Source/Platform/Windows/ShaderBuilder_Traits_Windows.h @@ -8,3 +8,4 @@ #pragma once #define AZ_TRAIT_ATOM_SHADERBUILDER_AZSLC "azslc.exe" +#define AZ_TRAIT_ATOM_FALLBACK_ASSET_HOST_PLATFORM AzFramework::PlatformId::PC diff --git a/Gems/Atom/Asset/Shader/Code/Source/Platform/iOS/ShaderBuilder_Traits_iOS.h b/Gems/Atom/Asset/Shader/Code/Source/Platform/iOS/ShaderBuilder_Traits_iOS.h index f8d93059f3..86afcd201c 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Platform/iOS/ShaderBuilder_Traits_iOS.h +++ b/Gems/Atom/Asset/Shader/Code/Source/Platform/iOS/ShaderBuilder_Traits_iOS.h @@ -8,3 +8,4 @@ #pragma once #define AZ_TRAIT_ATOM_SHADERBUILDER_AZSLC UNUSED_TRAIT +#define AZ_TRAIT_ATOM_FALLBACK_ASSET_HOST_PLATFORM UNUSED_TRAIT diff --git a/Gems/Atom/Asset/Shader/gem.json b/Gems/Atom/Asset/Shader/gem.json index eadf34acff..6d5e9f4dbe 100644 --- a/Gems/Atom/Asset/Shader/gem.json +++ b/Gems/Atom/Asset/Shader/gem.json @@ -8,7 +8,10 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI", + "Atom_RPI" + ] } diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp index e3749175bc..8a6b7db1ca 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp @@ -176,7 +176,10 @@ namespace AZ m_isAssetCatalogLoaded = true; - RPI::RPISystemInterface::Get()->InitializeSystemAssets(); + if (!RPI::RPISystemInterface::Get()->IsInitialized()) + { + RPI::RPISystemInterface::Get()->InitializeSystemAssets(); + } if (!RPI::RPISystemInterface::Get()->IsInitialized()) { diff --git a/Gems/Atom/Bootstrap/gem.json b/Gems/Atom/Bootstrap/gem.json index d5f287eb7f..5e98a1887d 100644 --- a/Gems/Atom/Bootstrap/gem.json +++ b/Gems/Atom/Bootstrap/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI" + ] } diff --git a/Gems/Atom/Component/DebugCamera/gem.json b/Gems/Atom/Component/DebugCamera/gem.json index cb2c597b07..586cb37058 100644 --- a/Gems/Atom/Component/DebugCamera/gem.json +++ b/Gems/Atom/Component/DebugCamera/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI" + ] } 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 2f84aaf5b2..4a66c52bf2 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli @@ -104,6 +104,7 @@ option bool o_layer3_enabled; enum class DebugDrawMode { None, BlendMask, Displacement, FinalBlendWeights }; option DebugDrawMode o_debugDrawMode; +// If you modify this enum, you must update the BlendSourceUsesDisplacement function in StandardMultilayerPBR_Displacement.lua enum class LayerBlendSource { BlendMaskTexture, BlendMaskVertexColors, Displacement, Displacement_With_BlendMaskTexture, Displacement_With_BlendMaskVertexColors, Fallback }; option LayerBlendSource o_layerBlendSource; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Displacement.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Displacement.lua index 8fa010f7bd..a032479d19 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Displacement.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Displacement.lua @@ -88,7 +88,7 @@ end -- @return a table with two values {min,max}. Negative values are below the surface and positive values are above the surface. function CalcOverallHeightRange(context) - local heightMinMax = {nil, nil} + local heightMinMax = {} local function GetMergedHeightRange(heightMinMax, offset, factor) top = offset @@ -138,7 +138,8 @@ function CalcOverallHeightRange(context) if(enableLayer3) then GetMergedHeightRange(heightMinMax, offsetLayer3, factorLayer3) end else - heightMinMax = {0,0} + heightMinMax[0] = 0 + heightMinMax[1] = 0 end return heightMinMax diff --git a/Gems/Atom/Feature/Common/Assets/Passes/HDRColorGrading.pass b/Gems/Atom/Feature/Common/Assets/Passes/HDRColorGrading.pass index f1e8304b30..adcd43ce83 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/HDRColorGrading.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/HDRColorGrading.pass @@ -5,7 +5,7 @@ "ClassData": { "PassTemplate": { "Name": "HDRColorGradingTemplate", - "PassClass": "FullScreenTriangle", + "PassClass": "HDRColorGradingPass", "Slots": [ { "Name": "Input", @@ -85,7 +85,7 @@ }, { "Name": "m_whiteBalanceKelvin", - "Value": 6500.0 // 1000.0f ... 40000.0f kelvin + "Value": 6600.0 // 1000.0f ... 40000.0f kelvin }, { "Name": "m_whiteBalanceTint", @@ -96,7 +96,7 @@ "Value": 0.0 // -1 ... 1 }, { - "Name": "m_splitToneMix", + "Name": "m_splitToneWeight", "Value": 0.0 // 0 ... 1 }, { @@ -120,7 +120,7 @@ "Value": 1.0 // 0 ... 1 }, { - "Name": "m_smhMix", + "Name": "m_smhWeight", "Value": 0.0 // 0 ... 1 } ], diff --git a/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass index ff55ebc200..eec3634045 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass @@ -92,8 +92,8 @@ ] }, { - "Name": "LookModificationTransformPass", - "TemplateName": "LookModificationTransformTemplate", + "Name": "HDRColorGradingPass", + "TemplateName": "HDRColorGradingTemplate", "Enabled": true, "Connections": [ { @@ -102,6 +102,20 @@ "Pass": "Parent", "Attachment": "LightingInput" } + } + ] + }, + { + "Name": "LookModificationTransformPass", + "TemplateName": "LookModificationTransformTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "Input", + "AttachmentRef": { + "Pass": "HDRColorGradingPass", + "Attachment": "Output" + } }, { "LocalSlot": "EyeAdaptationDataInput", diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli index ccaf5cf5d0..7a2d2ee428 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli @@ -40,11 +40,10 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject //! Reflection Probe (smallest probe volume that overlaps the object position) struct ReflectionProbeData { - float3 m_aabbPos; - float3 m_outerAabbMin; - float3 m_outerAabbMax; - float3 m_innerAabbMin; - float3 m_innerAabbMax; + row_major float3x4 m_modelToWorld; + row_major float3x4 m_modelToWorldInverse; // does not include extents + float3 m_outerObbHalfLengths; + float3 m_innerObbHalfLengths; float m_padding; bool m_useReflectionProbe; bool m_useParallaxCorrection; @@ -52,4 +51,32 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject ReflectionProbeData m_reflectionProbeData; TextureCube m_reflectionProbeCubeMap; + + float4x4 GetReflectionProbeWorldMatrix() + { + float4x4 modelToWorld = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorld[0] = m_reflectionProbeData.m_modelToWorld[0]; + modelToWorld[1] = m_reflectionProbeData.m_modelToWorld[1]; + modelToWorld[2] = m_reflectionProbeData.m_modelToWorld[2]; + return modelToWorld; + } + + float4x4 GetReflectionProbeWorldMatrixInverse() + { + float4x4 modelToWorldInverse = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorldInverse[0] = m_reflectionProbeData.m_modelToWorldInverse[0]; + modelToWorldInverse[1] = m_reflectionProbeData.m_modelToWorldInverse[1]; + modelToWorldInverse[2] = m_reflectionProbeData.m_modelToWorldInverse[2]; + return modelToWorldInverse; + } } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/LightingUtils.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/LightingUtils.azsli index 503095020c..3248fc83eb 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/LightingUtils.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/LightingUtils.azsli @@ -50,10 +50,10 @@ float GetRoughnessMip(float roughness) return roughness * maxRoughnessMip; } -// compute parallax corrected reflection vector +// compute parallax corrected reflection vector, AABB version // we do this by finding the intersection with the volume and adjusting the reflection vector for the surface position // https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ -float3 ApplyParallaxCorrection(float3 aabbMin, float3 aabbMax, float3 aabbPos, float3 positionWS, float3 reflectDir) +float3 ApplyParallaxCorrectionAABB(float3 aabbMin, float3 aabbMax, float3 aabbPos, float3 positionWS, float3 reflectDir) { float3 rcpReflectDir = 1.0f / reflectDir; float3 intersectA = (aabbMax - positionWS) * rcpReflectDir; @@ -63,3 +63,10 @@ float3 ApplyParallaxCorrection(float3 aabbMin, float3 aabbMax, float3 aabbPos, f float3 intersectPos = reflectDir * distance + positionWS; return (intersectPos - aabbPos); } + +// compute parallax corrected reflection vector, OBB version +float3 ApplyParallaxCorrectionOBB(float4x4 obbTransformInverse, float3 obbHalfExtents, float3 positionWS, float3 reflectDir) +{ + float4 p = mul(obbTransformInverse, float4(positionWS, 1.0f)); + return ApplyParallaxCorrectionAABB(-obbHalfExtents, obbHalfExtents, float3(0.0f, 0.0f, 0.0f), p, reflectDir); +} diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli index 5a0e150dbb..d33a307dfe 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli @@ -48,10 +48,9 @@ float3 GetIblSpecular( { if (ObjectSrg::m_reflectionProbeData.m_useParallaxCorrection) { - reflectDir = ApplyParallaxCorrection( - ObjectSrg::m_reflectionProbeData.m_outerAabbMin, - ObjectSrg::m_reflectionProbeData.m_outerAabbMax, - ObjectSrg::m_reflectionProbeData.m_aabbPos, + reflectDir = ApplyParallaxCorrectionOBB( + ObjectSrg::GetReflectionProbeWorldMatrixInverse(), + ObjectSrg::m_reflectionProbeData.m_outerObbHalfLengths, position, reflectDir); } @@ -60,11 +59,10 @@ float3 GetIblSpecular( probeSpecular *= (specularF0 * brdf.x + brdf.y); // compute blend amount based on world position in the reflection probe volume - float blendAmount = ComputeLerpBetweenInnerOuterAABBs( - ObjectSrg::m_reflectionProbeData.m_innerAabbMin, - ObjectSrg::m_reflectionProbeData.m_innerAabbMax, - ObjectSrg::m_reflectionProbeData.m_outerAabbMax, - ObjectSrg::m_reflectionProbeData.m_aabbPos, + float blendAmount = ComputeLerpBetweenInnerOuterOBBs( + ObjectSrg::GetReflectionProbeWorldMatrixInverse(), + ObjectSrg::m_reflectionProbeData.m_innerObbHalfLengths, + ObjectSrg::m_reflectionProbeData.m_outerObbHalfLengths, position); outSpecular = lerp(outSpecular, probeSpecular, blendAmount); diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.azsl index 9ae4a2bd68..cfa149f0d5 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/PostProcessing/HDRColorGrading.azsl @@ -50,13 +50,13 @@ ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback float m_whiteBalanceKelvin; float m_whiteBalanceTint; float m_splitToneBalance; - float m_splitToneMix; + float m_splitToneWeight; float m_colorGradingPostSaturation; float m_smhShadowsStart; float m_smhShadowsEnd; float m_smhHighlightsStart; float m_smhHighlightsEnd; - float m_smhMix; + float m_smhWeight; float3 m_channelMixingRed; float3 m_channelMixingGreen; @@ -69,9 +69,6 @@ ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback float4 m_smhShadowsColor; float4 m_smhMidtonesColor; float4 m_smhHighlightsColor; - - // my color grading output - float4 m_color; } float SaturateWithEpsilon(float value) @@ -133,7 +130,7 @@ float3 NoNanPow(float3 base, float3 power) return pow(max(abs(base), float3(FloatEpsilon, FloatEpsilon, FloatEpsilon)), power); } -float3 ColorGradeSplitTone (float3 frameColor, float balance, float mix) +float3 ColorGradeSplitTone (float3 frameColor, float balance, float weight) { float3 frameSplitTone = NoNanPow(frameColor, 1.0 / 2.2); const float t = SaturateWithEpsilon(CalculateLuminance(SaturateWithEpsilon(frameSplitTone), ColorSpaceId::ACEScg) + balance); @@ -142,7 +139,7 @@ float3 ColorGradeSplitTone (float3 frameColor, float balance, float mix) frameSplitTone = BlendMode_SoftLight(frameSplitTone, shadows); frameSplitTone = BlendMode_SoftLight(frameSplitTone, highlights); frameSplitTone = NoNanPow(frameSplitTone, 2.2); - return lerp(frameColor.rgb, frameSplitTone.rgb, mix); + return lerp(frameColor.rgb, frameSplitTone.rgb, weight); } float3 ColorGradeChannelMixer (float3 frameColor) @@ -154,7 +151,7 @@ float3 ColorGradeChannelMixer (float3 frameColor) } float3 ColorGradeShadowsMidtonesHighlights (float3 frameColor, float shadowsStart, float shadowsEnd, - float highlightsStart, float highlightsEnd, float mix, + float highlightsStart, float highlightsEnd, float weight, float4 shadowsColor, float4 midtonesColor, float4 highlightsColor) { const float3 shadowsColorACEScg = TransformColor(shadowsColor.rgb, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg); @@ -169,7 +166,7 @@ float3 ColorGradeShadowsMidtonesHighlights (float3 frameColor, float shadowsStar const float3 frameSmh = frameColor * shadowsColorACEScg * shadowsWeight + frameColor * midtonesColorACEScg * midtonesWeight + frameColor * highlightsColorACEScg * highlightsWeight; - return lerp(frameColor.rgb, frameSmh.rgb, mix); + return lerp(frameColor.rgb, frameSmh.rgb, weight); } float3 ColorGrade (float3 frameColor) @@ -181,11 +178,11 @@ float3 ColorGrade (float3 frameColor) PassSrg::m_colorFilterMultiply); frameColor = max(frameColor, 0.0); frameColor = ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPreSaturation); - frameColor = ColorGradeSplitTone(frameColor, PassSrg::m_splitToneBalance, PassSrg::m_splitToneMix); + frameColor = ColorGradeSplitTone(frameColor, PassSrg::m_splitToneBalance, PassSrg::m_splitToneWeight); frameColor = ColorGradeChannelMixer(frameColor); frameColor = max(frameColor, 0.0); frameColor = ColorGradeShadowsMidtonesHighlights(frameColor, PassSrg::m_smhShadowsStart, PassSrg::m_smhShadowsEnd, - PassSrg::m_smhHighlightsStart, PassSrg::m_smhHighlightsEnd, PassSrg::m_smhMix, + PassSrg::m_smhHighlightsStart, PassSrg::m_smhHighlightsEnd, PassSrg::m_smhWeight, PassSrg::m_smhShadowsColor, PassSrg::m_smhMidtonesColor, PassSrg::m_smhHighlightsColor); frameColor = ColorGradeHueShift(frameColor, PassSrg::m_colorGradingHueShift); frameColor = ColorGradeSaturation(frameColor, PassSrg::m_colorGradingPostSaturation); diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.azsl index 095576caaf..0fca4d03c6 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeBlendWeight.azsl @@ -63,7 +63,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // make sure the pixel belongs to this probe volume // this is necessary since it could have the correct stencil value but actually reside // in another volume that's in between the camera and the volume we're rendering - if (!AabbContainsPoint(ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, positionWS)) + if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_outerObbHalfLengths, positionWS)) { discard; } @@ -71,11 +71,15 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // determine blend based on position with respect to the inner and outer AABBs // if it's inside the inner AABB it blends at 100%, otherwise it's the percentage of the distance between the inner/outer AABB float blendWeight = 1.0f; - if (!AabbContainsPoint(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, positionWS)) + if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, positionWS)) { // not inside the inner AABB, so it's in between the inner and outer AABBs // compute blend amount based on the distance to the outer AABB - blendWeight = ComputeLerpBetweenInnerOuterAABBs(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS); + blendWeight = ComputeLerpBetweenInnerOuterOBBs( + ObjectSrg::GetWorldMatrixInverse(), + ObjectSrg::m_innerObbHalfLengths, + ObjectSrg::m_outerObbHalfLengths, + positionWS); } // write the blend weight (additive) at this position for the probe volume diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderCommon.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderCommon.azsli index f172035177..ce4684b150 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderCommon.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderCommon.azsli @@ -12,12 +12,12 @@ #include // compute final probe specular using the probe cubemap and the roughness, normals, and specularF0 for the surface -bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float3 aabbMin, float3 aabbMax, uint sampleIndex, out float3 specular) +bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float4x4 obbTransformInverse, float3 outerObbHalfLengths, uint sampleIndex, out float3 specular) { // make sure the pixel belongs to this probe volume // this is necessary since it could have the correct stencil value but actually reside // in another volume that's in between the camera and the volume we're rendering - if (!AabbContainsPoint(aabbMin, aabbMax, positionWS)) + if (!ObbContainsPoint(obbTransformInverse, outerObbHalfLengths, positionWS)) { return false; } @@ -47,7 +47,11 @@ bool ComputeProbeSpecular(float2 screenCoords, float3 positionWS, float3 aabbMin float3 localReflectDir = reflectDir; if (ObjectSrg::m_useParallaxCorrection) { - localReflectDir = ApplyParallaxCorrection(ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS, reflectDir); + localReflectDir = ApplyParallaxCorrectionOBB( + ObjectSrg::GetWorldMatrixInverse(), + ObjectSrg::m_outerObbHalfLengths, + positionWS, + reflectDir); } // sample reflection cubemap with the appropriate roughness mip diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl index 209605be03..a0734caf02 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderInner.azsl @@ -75,7 +75,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // compute specular using the probe cubemap and the roughness, normals, and specularF0 for the surface float3 specular = float3(0.0f, 0.0f, 0.0f); - if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, sampleIndex, specular)) + if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, sampleIndex, specular)) { discard; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli index 92ecd82dee..8151ed2fd5 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderObjectSrg.azsli @@ -13,12 +13,9 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject { row_major float3x4 m_modelToWorld; - - float3 m_aabbPos; - float3 m_outerAabbMin; - float3 m_outerAabbMax; - float3 m_innerAabbMin; - float3 m_innerAabbMax; + row_major float3x4 m_modelToWorldInverse; // does not include extents + float3 m_outerObbHalfLengths; + float3 m_innerObbHalfLengths; bool m_useParallaxCorrection; TextureCube m_reflectionCubeMap; @@ -35,4 +32,18 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject modelToWorld[2] = ObjectSrg::m_modelToWorld[2]; return modelToWorld; } + + float4x4 GetWorldMatrixInverse() + { + float4x4 modelToWorldInverse = float4x4( + float4(1, 0, 0, 0), + float4(0, 1, 0, 0), + float4(0, 0, 1, 0), + float4(0, 0, 0, 1)); + + modelToWorldInverse[0] = ObjectSrg::m_modelToWorldInverse[0]; + modelToWorldInverse[1] = ObjectSrg::m_modelToWorldInverse[1]; + modelToWorldInverse[2] = ObjectSrg::m_modelToWorldInverse[2]; + return modelToWorldInverse; + } } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl index be94a53588..ac97172f1f 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/Reflections/ReflectionProbeRenderOuter.azsl @@ -77,7 +77,7 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // compute specular using the probe cubemap and the roughness, normals, and specularF0 for the surface float3 specular = float3(0.0f, 0.0f, 0.0f); - if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::m_outerAabbMin, ObjectSrg::m_outerAabbMax, sampleIndex, specular)) + if (!ComputeProbeSpecular(IN.m_position.xy, positionWS, ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_outerObbHalfLengths, sampleIndex, specular)) { discard; } @@ -85,13 +85,17 @@ PSOutput MainPS(VSOutput IN, in uint sampleIndex : SV_SampleIndex) // determine blend based on position with respect to the inner and outer AABBs // if it's inside the inner AABB it blends at 100%, otherwise it's the percentage of the distance between the inner/outer AABB float blendWeight = 1.0f; - if (!AabbContainsPoint(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, positionWS)) + if (!ObbContainsPoint(ObjectSrg::GetWorldMatrixInverse(), ObjectSrg::m_innerObbHalfLengths, positionWS)) { // not inside the inner AABB, so it's in between the inner and outer AABBs // compute blend amount based on the distance to the outer AABB - blendWeight = ComputeLerpBetweenInnerOuterAABBs(ObjectSrg::m_innerAabbMin, ObjectSrg::m_innerAabbMax, ObjectSrg::m_outerAabbMax, ObjectSrg::m_aabbPos, positionWS); + blendWeight = ComputeLerpBetweenInnerOuterOBBs( + ObjectSrg::GetWorldMatrixInverse(), + ObjectSrg::m_innerObbHalfLengths, + ObjectSrg::m_outerObbHalfLengths, + positionWS); } - + // retrieve the blend weight of all probes at this location float blendWeightAllProbes = PassSrg::m_blendWeight.Load(IN.m_position.xy, sampleIndex).r; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl new file mode 100644 index 0000000000..241ba3b5de --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl @@ -0,0 +1,37 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +// Macros below are of the form: +// PARAM(NAME, MEMBER_NAME, DEFAULT_VALUE, ...) + +AZ_GFX_BOOL_PARAM(Enabled, m_enabled, false) +AZ_GFX_FLOAT_PARAM(ColorGradingExposure, m_colorGradingExposure, 0.0) +AZ_GFX_FLOAT_PARAM(ColorGradingContrast, m_colorGradingContrast, 0.0) +AZ_GFX_FLOAT_PARAM(ColorGradingHueShift, m_colorGradingHueShift, 0.0) +AZ_GFX_FLOAT_PARAM(ColorGradingPreSaturation, m_colorGradingPreSaturation, 1.0) +AZ_GFX_FLOAT_PARAM(ColorGradingFilterIntensity, m_colorGradingFilterIntensity, 1.0) +AZ_GFX_FLOAT_PARAM(ColorGradingFilterMultiply, m_colorGradingFilterMultiply, 0.0) +AZ_GFX_FLOAT_PARAM(ColorGradingPostSaturation, m_colorGradingPostSaturation, 1.0) +AZ_GFX_FLOAT_PARAM(WhiteBalanceKelvin, m_whiteBalanceKelvin, 6600.0) +AZ_GFX_FLOAT_PARAM(WhiteBalanceTint, m_whiteBalanceTint, 0.0) +AZ_GFX_FLOAT_PARAM(SplitToneBalance, m_splitToneBalance, 0.0) +AZ_GFX_FLOAT_PARAM(SplitToneWeight, m_splitToneWeight, 0.0) +AZ_GFX_FLOAT_PARAM(SmhShadowsStart, m_smhShadowsStart, 0.0) +AZ_GFX_FLOAT_PARAM(SmhShadowsEnd, m_smhShadowsEnd, 0.3) +AZ_GFX_FLOAT_PARAM(SmhHighlightsStart, m_smhHighlightsStart, 0.55) +AZ_GFX_FLOAT_PARAM(SmhHighlightsEnd, m_smhHighlightsEnd, 1.0) +AZ_GFX_FLOAT_PARAM(SmhWeight, m_smhWeight, 0.0) +AZ_GFX_VEC3_PARAM(ChannelMixingRed, m_channelMixingRed, AZ::Vector3(1.0f, 0.0f, 0.0f)) +AZ_GFX_VEC3_PARAM(ChannelMixingGreen, m_channelMixingGreen, AZ::Vector3(0.0f, 1.0f, 0.0f)) +AZ_GFX_VEC3_PARAM(ChannelMixingBlue, m_channelMixingBlue, AZ::Vector3(0.0f, 0.f, 1.0f)) +AZ_GFX_VEC3_PARAM(ColorFilterSwatch, m_colorFilterSwatch, AZ::Vector3(1.0f, 0.5f, 0.5f)) +AZ_GFX_VEC3_PARAM(SplitToneShadowsColor, m_splitToneShadowsColor, AZ::Vector3(1.0f, 0.5f, 0.5f)) +AZ_GFX_VEC3_PARAM(SplitToneHighlightsColor, m_splitToneHighlightsColor, AZ::Vector3(0.1f, 1.0f, 0.1f)) +AZ_GFX_VEC3_PARAM(SmhShadowsColor, m_smhShadowsColor, AZ::Vector3(1.0f, 0.25f, 0.25f)) +AZ_GFX_VEC3_PARAM(SmhMidtonesColor, m_smhMidtonesColor, AZ::Vector3(0.1f, 0.1f, 1.0f)) +AZ_GFX_VEC3_PARAM(SmhHighlightsColor, m_smhHighlightsColor, AZ::Vector3(1.0f, 0.0f, 1.0f)) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingSettingsInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingSettingsInterface.h new file mode 100644 index 0000000000..019df206eb --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingSettingsInterface.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class HDRColorGradingSettingsInterface + { + public: + AZ_RTTI(AZ::Render::HDRColorGradingSettingsInterface, "{CB5ADF78-27DE-438C-A991-1E5433046A42}"); + + // Auto-gen virtual getter and setter functions... +#include +#include +#include + + virtual void OnConfigChanged() = 0; + }; + } +} diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettings.inl b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettings.inl index 0259f07525..8409732f34 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettings.inl +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettings.inl @@ -25,3 +25,4 @@ POST_PROCESS_MEMBER(ExposureControlSettings, m_exposureControlSettings) POST_PROCESS_MEMBER(SsaoSettings, m_ssaoSettings) POST_PROCESS_MEMBER(LookModificationSettings, m_lookModificationSettings) POST_PROCESS_MEMBER(DeferredFogSettings, m_deferredFogSettings) +POST_PROCESS_MEMBER(HDRColorGradingSettings, m_hdrColorGradingSettings) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettingsInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettingsInterface.h index 12c0017aad..905aa9f2ca 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettingsInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/PostProcess/PostProcessSettingsInterface.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp index 2057c596e5..29db7d6673 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp @@ -9,8 +9,6 @@ #include "AuxGeomDrawQueue.h" -#include - #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomFeatureProcessor.cpp index ee6d3ba4a3..a4720ad131 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomFeatureProcessor.cpp @@ -12,7 +12,6 @@ #include "DynamicPrimitiveProcessor.h" #include "FixedShapeProcessor.h" -#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/DynamicPrimitiveProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/DynamicPrimitiveProcessor.cpp index 9af6493413..ae9e55ac8d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/DynamicPrimitiveProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/DynamicPrimitiveProcessor.cpp @@ -9,7 +9,6 @@ #include "DynamicPrimitiveProcessor.h" #include "AuxGeomDrawProcessorShared.h" -#include #include #include #include @@ -21,6 +20,8 @@ #include #include +#include + namespace AZ { namespace Render diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/FixedShapeProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/FixedShapeProcessor.cpp index 8b5e439ca0..c2ee397b4c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/FixedShapeProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/FixedShapeProcessor.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index de03c08c6f..db49fba96f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -222,6 +223,7 @@ namespace AZ passSystem->AddPassCreator(Name("LightCullingRemapPass"), &LightCullingRemap::Create); passSystem->AddPassCreator(Name("LightCullingTilePreparePass"), &LightCullingTilePreparePass::Create); passSystem->AddPassCreator(Name("BlendColorGradingLutsPass"), &BlendColorGradingLutsPass::Create); + passSystem->AddPassCreator(Name("HDRColorGradingPass"), &HDRColorGradingPass::Create); passSystem->AddPassCreator(Name("LookModificationCompositePass"), &LookModificationCompositePass::Create); passSystem->AddPassCreator(Name("LookModificationTransformPass"), &LookModificationPass::Create); passSystem->AddPassCreator(Name("SMAAEdgeDetectionPass"), &SMAAEdgeDetectionPass::Create); diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/CapsuleLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/CapsuleLightFeatureProcessor.cpp index 849c930afd..bbea462ac8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/CapsuleLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/CapsuleLightFeatureProcessor.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index 702818dcd9..42cca0e57c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp index ca2c038e46..26e1757a5a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp @@ -16,7 +16,6 @@ #include -#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp index bfb0b1252b..d3b5646e0b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PolygonLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PolygonLightFeatureProcessor.cpp index 9dbf01ae26..f3c05eeeec 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PolygonLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PolygonLightFeatureProcessor.cpp @@ -16,7 +16,6 @@ #include -#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/QuadLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/QuadLightFeatureProcessor.cpp index aadc8c2020..787e150646 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/QuadLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/QuadLightFeatureProcessor.cpp @@ -16,7 +16,6 @@ #include -#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimplePointLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimplePointLightFeatureProcessor.cpp index f49a5e94fc..bc13d3d508 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimplePointLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimplePointLightFeatureProcessor.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimpleSpotLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimpleSpotLightFeatureProcessor.cpp index 277b5026d7..49b7f7d12c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimpleSpotLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimpleSpotLightFeatureProcessor.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp index 4ff718b5ad..f00c902a73 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp @@ -10,7 +10,6 @@ #include -#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp index 1a68317532..0ff8d2c165 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp @@ -15,7 +15,6 @@ #include -#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/ImageBasedLights/ImageBasedLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ImageBasedLights/ImageBasedLightFeatureProcessor.cpp index 9cdad34f17..6a2eda8f22 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImageBasedLights/ImageBasedLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ImageBasedLights/ImageBasedLightFeatureProcessor.cpp @@ -11,8 +11,6 @@ #include #include -#include - #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index fe201f9ca8..82568126df 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -8,7 +8,6 @@ #include -#include #include #include #include @@ -1111,20 +1110,17 @@ namespace AZ if (reflectionProbeFeatureProcessor && (m_descriptor.m_useForwardPassIblSpecular || m_hasForwardPassIblSpecularMaterial)) { // retrieve probe constant indices - AZ::RHI::ShaderInputConstantIndex posConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_aabbPos")); - AZ_Error("MeshDataInstance", posConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); + AZ::RHI::ShaderInputConstantIndex modelToWorldConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_modelToWorld")); + AZ_Error("MeshDataInstance", modelToWorldConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); - AZ::RHI::ShaderInputConstantIndex outerAabbMinConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerAabbMin")); - AZ_Error("MeshDataInstance", outerAabbMinConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); + AZ::RHI::ShaderInputConstantIndex modelToWorldInverseConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_modelToWorldInverse")); + AZ_Error("MeshDataInstance", modelToWorldInverseConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); - AZ::RHI::ShaderInputConstantIndex outerAabbMaxConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerAabbMax")); - AZ_Error("MeshDataInstance", outerAabbMaxConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); + AZ::RHI::ShaderInputConstantIndex outerObbHalfLengthsConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_outerObbHalfLengths")); + AZ_Error("MeshDataInstance", outerObbHalfLengthsConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); - AZ::RHI::ShaderInputConstantIndex innerAabbMinConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerAabbMin")); - AZ_Error("MeshDataInstance", innerAabbMinConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); - - AZ::RHI::ShaderInputConstantIndex innerAabbMaxConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerAabbMax")); - AZ_Error("MeshDataInstance", innerAabbMaxConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); + AZ::RHI::ShaderInputConstantIndex innerObbHalfLengthsConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_innerObbHalfLengths")); + AZ_Error("MeshDataInstance", innerObbHalfLengthsConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); AZ::RHI::ShaderInputConstantIndex useReflectionProbeConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_useReflectionProbe")); AZ_Error("MeshDataInstance", useReflectionProbeConstantIndex.IsValid(), "Failed to find ReflectionProbe constant index"); @@ -1146,11 +1142,10 @@ namespace AZ if (!reflectionProbes.empty() && reflectionProbes[0]) { - m_shaderResourceGroup->SetConstant(posConstantIndex, reflectionProbes[0]->GetPosition()); - m_shaderResourceGroup->SetConstant(outerAabbMinConstantIndex, reflectionProbes[0]->GetOuterAabbWs().GetMin()); - m_shaderResourceGroup->SetConstant(outerAabbMaxConstantIndex, reflectionProbes[0]->GetOuterAabbWs().GetMax()); - m_shaderResourceGroup->SetConstant(innerAabbMinConstantIndex, reflectionProbes[0]->GetInnerAabbWs().GetMin()); - m_shaderResourceGroup->SetConstant(innerAabbMaxConstantIndex, reflectionProbes[0]->GetInnerAabbWs().GetMax()); + m_shaderResourceGroup->SetConstant(modelToWorldConstantIndex, reflectionProbes[0]->GetTransform()); + m_shaderResourceGroup->SetConstant(modelToWorldInverseConstantIndex, Matrix3x4::CreateFromTransform(reflectionProbes[0]->GetTransform()).GetInverseFull()); + m_shaderResourceGroup->SetConstant(outerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetOuterObbWs().GetHalfLengths()); + m_shaderResourceGroup->SetConstant(innerObbHalfLengthsConstantIndex, reflectionProbes[0]->GetInnerObbWs().GetHalfLengths()); m_shaderResourceGroup->SetConstant(useReflectionProbeConstantIndex, true); m_shaderResourceGroup->SetConstant(useParallaxCorrectionConstantIndex, reflectionProbes[0]->GetUseParallaxCorrection()); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ColorGrading/HDRColorGradingSettings.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ColorGrading/HDRColorGradingSettings.cpp new file mode 100644 index 0000000000..4542b53a15 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ColorGrading/HDRColorGradingSettings.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#include + +#include +#include + +namespace AZ +{ + namespace Render + { + HDRColorGradingSettings::HDRColorGradingSettings(PostProcessFeatureProcessor* featureProcessor) + : PostProcessBase(featureProcessor) + { + } + + void HDRColorGradingSettings::OnConfigChanged() + { + m_parentSettings->OnConfigChanged(); + } + + void HDRColorGradingSettings::ApplySettingsTo(HDRColorGradingSettings* target, [[maybe_unused]] float alpha) const + { + AZ_Assert(target != nullptr, "HDRColorGradingSettings::ApplySettingsTo called with nullptr as argument."); + + if (GetEnabled()) + { + target->m_enabled = m_enabled; + +#define AZ_GFX_BOOL_PARAM(NAME, MEMBER_NAME, DefaultValue) ; +#define AZ_GFX_FLOAT_PARAM(NAME, MEMBER_NAME, DefaultValue) \ + { \ + target->Set##NAME(AZ::Lerp(target->MEMBER_NAME, MEMBER_NAME, alpha)); \ + } + +#define AZ_GFX_VEC3_PARAM(NAME, MEMBER_NAME, DefaultValue) \ + { \ + target->MEMBER_NAME.Set(AZ::Lerp(target->MEMBER_NAME.GetX(), MEMBER_NAME.GetX(), alpha), \ + AZ::Lerp(target->MEMBER_NAME.GetY(), MEMBER_NAME.GetY(), alpha), \ + AZ::Lerp(target->MEMBER_NAME.GetZ(), MEMBER_NAME.GetZ(), alpha)); \ + } + +#include +#include + } + } + + void HDRColorGradingSettings::Simulate([[maybe_unused]] float deltaTime) + { + } + } // namespace Render +} // namespace AZ + + diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/ColorGrading/HDRColorGradingSettings.h b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ColorGrading/HDRColorGradingSettings.h new file mode 100644 index 0000000000..2358919c28 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/ColorGrading/HDRColorGradingSettings.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include + +#include + +namespace AZ +{ + namespace Render + { + class PostProcessSettings; + + class HDRColorGradingSettings final + : public HDRColorGradingSettingsInterface + , public PostProcessBase + { + friend class PostProcessSettings; + friend class PostProcessFeatureProcessor; + + public: + AZ_RTTI(HDRColorGradingSettings, "{EA8C05D4-66D0-4141-8D4D-68E5D764C2ED}", HDRColorGradingSettingsInterface, PostProcessBase); + AZ_CLASS_ALLOCATOR(HDRColorGradingSettings, SystemAllocator, 0); + + HDRColorGradingSettings(PostProcessFeatureProcessor* featureProcessor); + ~HDRColorGradingSettings() = default; + + void OnConfigChanged() override; + + void ApplySettingsTo(HDRColorGradingSettings* target, float alpha) const; + + // Generate all getters and override setters. + // Declare non-override setters, which will be defined in the .cpp +#define AZ_GFX_COMMON_PARAM(ValueType, Name, MemberName, DefaultValue) \ + ValueType Get##Name() const override { return MemberName; } \ + void Set##Name(ValueType val) override \ + { \ + MemberName = val; \ + } \ + +#define AZ_GFX_COMMON_OVERRIDE(ValueType, Name, MemberName, OverrideValueType) \ + OverrideValueType Get##Name##Override() const override { return MemberName##Override; } \ + void Set##Name##Override(OverrideValueType val) override { MemberName##Override = val; } \ + +#include +#include +#include + + private: + // Generate members... +#include +#include +#include + + void Simulate(float deltaTime); + + PostProcessSettings* m_parentSettings = nullptr; + }; + } +} diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessFeatureProcessor.cpp index d5b3ebd63b..a9d8d5105f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessFeatureProcessor.cpp @@ -8,8 +8,6 @@ #include -#include - #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessSettings.h b/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessSettings.h index 515326e357..c2a32e6cee 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessSettings.h +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessSettings.h @@ -17,6 +17,7 @@ #include #include #include +#include #include namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.cpp new file mode 100644 index 0000000000..62fff6b6fe --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include +#include +#include + + namespace AZ +{ + namespace Render + { + RPI::Ptr HDRColorGradingPass::Create(const RPI::PassDescriptor& descriptor) + { + RPI::Ptr pass = aznew HDRColorGradingPass(descriptor); + return AZStd::move(pass); + } + + HDRColorGradingPass::HDRColorGradingPass(const RPI::PassDescriptor& descriptor) + : AZ::RPI::FullscreenTrianglePass(descriptor) + { + } + + void HDRColorGradingPass::InitializeInternal() + { + FullscreenTrianglePass::InitializeInternal(); + + m_colorGradingExposureIndex.Reset(); + m_colorGradingContrastIndex.Reset(); + m_colorGradingHueShiftIndex.Reset(); + m_colorGradingPreSaturationIndex.Reset(); + m_colorFilterIntensityIndex.Reset(); + m_colorFilterMultiplyIndex.Reset(); + m_whiteBalanceKelvinIndex.Reset(); + m_whiteBalanceTintIndex.Reset(); + m_splitToneBalanceIndex.Reset(); + m_splitToneWeightIndex.Reset(); + m_colorGradingPostSaturationIndex.Reset(); + m_smhShadowsStartIndex.Reset(); + m_smhShadowsEndIndex.Reset(); + m_smhHighlightsStartIndex.Reset(); + m_smhHighlightsEndIndex.Reset(); + m_smhWeightIndex.Reset(); + + m_channelMixingRedIndex.Reset(); + m_channelMixingGreenIndex.Reset(); + m_channelMixingBlueIndex.Reset(); + + m_colorFilterSwatchIndex.Reset(); + m_splitToneShadowsColorIndex.Reset(); + m_splitToneHighlightsColorIndex.Reset(); + m_smhShadowsColorIndex.Reset(); + m_smhMidtonesColorIndex.Reset(); + m_smhHighlightsColorIndex.Reset(); + } + + void HDRColorGradingPass::FrameBeginInternal(FramePrepareParams params) + { + SetSrgConstants(); + + FullscreenTrianglePass::FrameBeginInternal(params); + } + + bool HDRColorGradingPass::IsEnabled() const + { + const auto* colorGradingSettings = GetHDRColorGradingSettings(); + return colorGradingSettings ? colorGradingSettings->GetEnabled() : false; + } + + void HDRColorGradingPass::SetSrgConstants() + { + const HDRColorGradingSettings* settings = GetHDRColorGradingSettings(); + if (settings) + { + m_shaderResourceGroup->SetConstant(m_colorGradingExposureIndex, settings->GetColorGradingExposure()); + m_shaderResourceGroup->SetConstant(m_colorGradingContrastIndex, settings->GetColorGradingContrast()); + m_shaderResourceGroup->SetConstant(m_colorGradingHueShiftIndex, settings->GetColorGradingHueShift()); + m_shaderResourceGroup->SetConstant(m_colorGradingPreSaturationIndex, settings->GetColorGradingPreSaturation()); + m_shaderResourceGroup->SetConstant(m_colorFilterIntensityIndex, settings->GetColorGradingFilterIntensity()); + m_shaderResourceGroup->SetConstant(m_colorFilterMultiplyIndex, settings->GetColorGradingFilterMultiply()); + m_shaderResourceGroup->SetConstant(m_whiteBalanceKelvinIndex, settings->GetWhiteBalanceKelvin()); + m_shaderResourceGroup->SetConstant(m_whiteBalanceTintIndex, settings->GetWhiteBalanceTint()); + m_shaderResourceGroup->SetConstant(m_splitToneBalanceIndex, settings->GetSplitToneBalance()); + m_shaderResourceGroup->SetConstant(m_splitToneWeightIndex, settings->GetSplitToneWeight()); + m_shaderResourceGroup->SetConstant(m_colorGradingPostSaturationIndex, settings->GetColorGradingPostSaturation()); + m_shaderResourceGroup->SetConstant(m_smhShadowsStartIndex, settings->GetSmhShadowsStart()); + m_shaderResourceGroup->SetConstant(m_smhShadowsEndIndex, settings->GetSmhShadowsEnd()); + m_shaderResourceGroup->SetConstant(m_smhHighlightsStartIndex, settings->GetSmhHighlightsStart()); + m_shaderResourceGroup->SetConstant(m_smhHighlightsEndIndex, settings->GetSmhHighlightsEnd()); + m_shaderResourceGroup->SetConstant(m_smhWeightIndex, settings->GetSmhWeight()); + + m_shaderResourceGroup->SetConstant(m_channelMixingRedIndex, settings->GetChannelMixingRed()); + m_shaderResourceGroup->SetConstant(m_channelMixingGreenIndex, settings->GetChannelMixingGreen()); + m_shaderResourceGroup->SetConstant(m_channelMixingBlueIndex, settings->GetChannelMixingBlue()); + + m_shaderResourceGroup->SetConstant(m_colorFilterSwatchIndex, AZ::Vector4(settings->GetColorFilterSwatch())); + m_shaderResourceGroup->SetConstant(m_splitToneShadowsColorIndex, AZ::Vector4(settings->GetSplitToneShadowsColor())); + m_shaderResourceGroup->SetConstant(m_splitToneHighlightsColorIndex, AZ::Vector4(settings->GetSplitToneHighlightsColor())); + m_shaderResourceGroup->SetConstant(m_smhShadowsColorIndex, AZ::Vector4(settings->GetSmhShadowsColor())); + m_shaderResourceGroup->SetConstant(m_smhMidtonesColorIndex, AZ::Vector4(settings->GetSmhMidtonesColor())); + m_shaderResourceGroup->SetConstant(m_smhHighlightsColorIndex, AZ::Vector4(settings->GetSmhHighlightsColor())); + } + } + + const AZ::Render::HDRColorGradingSettings* HDRColorGradingPass::GetHDRColorGradingSettings() const + { + RPI::Scene* scene = GetScene(); + if (scene) + { + PostProcessFeatureProcessor* fp = scene->GetFeatureProcessor(); + AZ::RPI::ViewPtr view = scene->GetDefaultRenderPipeline()->GetDefaultView(); + if (fp) + { + PostProcessSettings* postProcessSettings = fp->GetLevelSettingsFromView(view); + if (postProcessSettings) + { + const HDRColorGradingSettings* colorGradingSettings = postProcessSettings->GetHDRColorGradingSettings(); + if (colorGradingSettings != nullptr && colorGradingSettings->GetEnabled()) + { + return postProcessSettings->GetHDRColorGradingSettings(); + } + } + } + } + return nullptr; + } + } +} diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.h b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.h new file mode 100644 index 0000000000..e5eaf7db26 --- /dev/null +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/HDRColorGradingPass.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + /** + * The color grading pass. + */ + class HDRColorGradingPass + : public AZ::RPI::FullscreenTrianglePass + //TODO: , public PostProcessingShaderOptionBase + { + public: + AZ_RTTI(HDRColorGradingPass, "{E68E31A1-DB24-4AFF-A029-456A8B74C03C}", AZ::RPI::FullscreenTrianglePass); + AZ_CLASS_ALLOCATOR(HDRColorGradingPass, SystemAllocator, 0); + + virtual ~HDRColorGradingPass() = default; + + //! Creates a ColorGradingPass + static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); + + protected: + HDRColorGradingPass(const RPI::PassDescriptor& descriptor); + + //! Pass behavior overrides + void InitializeInternal() override; + void FrameBeginInternal(FramePrepareParams params) override; + bool IsEnabled() const override; + + private: + const HDRColorGradingSettings* GetHDRColorGradingSettings() const; + void SetSrgConstants(); + + RHI::ShaderInputNameIndex m_colorGradingExposureIndex = "m_colorGradingExposure"; + RHI::ShaderInputNameIndex m_colorGradingContrastIndex = "m_colorGradingContrast"; + RHI::ShaderInputNameIndex m_colorGradingHueShiftIndex = "m_colorGradingHueShift"; + RHI::ShaderInputNameIndex m_colorGradingPreSaturationIndex = "m_colorGradingPreSaturation"; + RHI::ShaderInputNameIndex m_colorFilterIntensityIndex = "m_colorFilterIntensity"; + RHI::ShaderInputNameIndex m_colorFilterMultiplyIndex = "m_colorFilterMultiply"; + RHI::ShaderInputNameIndex m_whiteBalanceKelvinIndex = "m_whiteBalanceKelvin"; + RHI::ShaderInputNameIndex m_whiteBalanceTintIndex = "m_whiteBalanceTint"; + RHI::ShaderInputNameIndex m_splitToneBalanceIndex = "m_splitToneBalance"; + RHI::ShaderInputNameIndex m_splitToneWeightIndex = "m_splitToneWeight"; + RHI::ShaderInputNameIndex m_colorGradingPostSaturationIndex = "m_colorGradingPostSaturation"; + RHI::ShaderInputNameIndex m_smhShadowsStartIndex = "m_smhShadowsStart"; + RHI::ShaderInputNameIndex m_smhShadowsEndIndex = "m_smhShadowsEnd"; + RHI::ShaderInputNameIndex m_smhHighlightsStartIndex = "m_smhHighlightsStart"; + RHI::ShaderInputNameIndex m_smhHighlightsEndIndex = "m_smhHighlightsEnd"; + RHI::ShaderInputNameIndex m_smhWeightIndex = "m_smhWeight"; + + RHI::ShaderInputNameIndex m_channelMixingRedIndex = "m_channelMixingRed"; + RHI::ShaderInputNameIndex m_channelMixingGreenIndex = "m_channelMixingGreen"; + RHI::ShaderInputNameIndex m_channelMixingBlueIndex = "m_channelMixingBlue"; + + RHI::ShaderInputNameIndex m_colorFilterSwatchIndex = "m_colorFilterSwatch"; + RHI::ShaderInputNameIndex m_splitToneShadowsColorIndex = "m_splitToneShadowsColor"; + RHI::ShaderInputNameIndex m_splitToneHighlightsColorIndex = "m_splitToneHighlightsColor"; + RHI::ShaderInputNameIndex m_smhShadowsColorIndex = "m_smhShadowsColor"; + RHI::ShaderInputNameIndex m_smhMidtonesColorIndex = "m_smhMidtonesColor"; + RHI::ShaderInputNameIndex m_smhHighlightsColorIndex = "m_smhHighlightsColor"; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp index a795d88212..eef0c51e95 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp @@ -16,8 +16,6 @@ #include -#include - #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp index 920326e081..a476b5b839 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp @@ -8,7 +8,6 @@ #include "ProfilingCaptureSystemComponent.h" -#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h index 1846767139..9f8a8a90c6 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h @@ -12,7 +12,6 @@ #include #include -#include namespace AZ { diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp index a17ecaf1aa..98d32b1050 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp index 778f06c3be..e86d91d387 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp @@ -138,43 +138,39 @@ namespace AZ if (m_updateSrg) { // stencil Srg - // Note: the stencil pass uses a slightly reduced inner AABB to avoid seams + // Note: the stencil pass uses a slightly reduced inner OBB to avoid seams Vector3 innerExtentsReduced = m_innerExtents - Vector3(0.1f, 0.1f, 0.1f); - Matrix3x4 modelToWorldStencil = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(innerExtentsReduced); + Matrix3x4 modelToWorldStencil = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(innerExtentsReduced); m_stencilSrg->SetConstant(m_reflectionRenderData->m_modelToWorldStencilConstantIndex, modelToWorldStencil); m_stencilSrg->Compile(); + Matrix3x4 modelToWorldInverse = Matrix3x4::CreateFromTransform(m_transform).GetInverseFull(); + // blend weight Srg - Matrix3x4 modelToWorldOuter = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_outerExtents); + Matrix3x4 modelToWorldOuter = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_outerExtents); m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldOuter); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter()); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin()); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax()); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin()); - m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax()); + m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse); + m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths()); + m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths()); m_blendWeightSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection); m_blendWeightSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage); m_blendWeightSrg->Compile(); // render outer Srg m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldOuter); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter()); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin()); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax()); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin()); - m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax()); + m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse); + m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths()); + m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths()); m_renderOuterSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection); m_renderOuterSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage); m_renderOuterSrg->Compile(); // render inner Srg - Matrix3x4 modelToWorldInner = Matrix3x4::CreateFromMatrix3x3AndTranslation(Matrix3x3::CreateIdentity(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_innerExtents); + Matrix3x4 modelToWorldInner = Matrix3x4::CreateFromQuaternionAndTranslation(m_transform.GetRotation(), m_transform.GetTranslation()) * Matrix3x4::CreateScale(m_innerExtents); m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_modelToWorldRenderConstantIndex, modelToWorldInner); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_aabbPosRenderConstantIndex, m_outerAabbWs.GetCenter()); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerAabbMinRenderConstantIndex, m_outerAabbWs.GetMin()); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerAabbMaxRenderConstantIndex, m_outerAabbWs.GetMax()); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerAabbMinRenderConstantIndex, m_innerAabbWs.GetMin()); - m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerAabbMaxRenderConstantIndex, m_innerAabbWs.GetMax()); + m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_modelToWorldInverseRenderConstantIndex, modelToWorldInverse); + m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_outerObbHalfLengthsRenderConstantIndex, m_outerObbWs.GetHalfLengths()); + m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_innerObbHalfLengthsRenderConstantIndex, m_innerObbWs.GetHalfLengths()); m_renderInnerSrg->SetConstant(m_reflectionRenderData->m_useParallaxCorrectionRenderConstantIndex, m_useParallaxCorrection); m_renderInnerSrg->SetImage(m_reflectionRenderData->m_reflectionCubeMapRenderImageIndex, m_cubeMapImage); m_renderInnerSrg->Compile(); @@ -244,22 +240,22 @@ namespace AZ m_outerExtents *= m_transform.GetUniformScale(); m_innerExtents *= m_transform.GetUniformScale(); - m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f); - m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f); + m_outerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_outerExtents / 2.0f); + m_innerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_innerExtents / 2.0f); m_updateSrg = true; } void ReflectionProbe::SetOuterExtents(const AZ::Vector3& outerExtents) { m_outerExtents = outerExtents * m_transform.GetUniformScale(); - m_outerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_outerExtents / 2.0f); + m_outerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_outerExtents / 2.0f); m_updateSrg = true; } void ReflectionProbe::SetInnerExtents(const AZ::Vector3& innerExtents) { m_innerExtents = innerExtents * m_transform.GetUniformScale(); - m_innerAabbWs = Aabb::CreateCenterHalfExtents(m_transform.GetTranslation(), m_innerExtents / 2.0f); + m_innerObbWs = Obb::CreateFromPositionRotationAndHalfLengths(m_transform.GetTranslation(), m_transform.GetRotation(), m_innerExtents / 2.0f); m_updateSrg = true; } @@ -410,13 +406,14 @@ namespace AZ lod.m_screenCoverageMax = 1.0f; // update cullable bounds + Aabb outerAabb = Aabb::CreateFromObb(m_outerObbWs); Vector3 center; float radius; - m_outerAabbWs.GetAsSphere(center, radius); + outerAabb.GetAsSphere(center, radius); m_cullable.m_cullData.m_boundingSphere = Sphere(center, radius); - m_cullable.m_cullData.m_boundingObb = m_outerAabbWs.GetTransformedObb(AZ::Transform::CreateIdentity()); - m_cullable.m_cullData.m_visibilityEntry.m_boundingVolume = m_outerAabbWs; + m_cullable.m_cullData.m_boundingObb = m_outerObbWs; + m_cullable.m_cullData.m_visibilityEntry.m_boundingVolume = outerAabb; m_cullable.m_cullData.m_visibilityEntry.m_userData = &m_cullable; m_cullable.m_cullData.m_visibilityEntry.m_typeFlags = AzFramework::VisibilityEntry::TYPE_RPI_Cullable; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h index bb30ce9470..5b56b70ec8 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.h @@ -55,15 +55,13 @@ namespace AZ RHI::DrawListTag m_renderOuterDrawListTag; RHI::DrawListTag m_renderInnerDrawListTag; - RHI::ShaderInputConstantIndex m_modelToWorldStencilConstantIndex; - RHI::ShaderInputConstantIndex m_modelToWorldRenderConstantIndex; - RHI::ShaderInputConstantIndex m_aabbPosRenderConstantIndex; - RHI::ShaderInputConstantIndex m_outerAabbMinRenderConstantIndex; - RHI::ShaderInputConstantIndex m_outerAabbMaxRenderConstantIndex; - RHI::ShaderInputConstantIndex m_innerAabbMinRenderConstantIndex; - RHI::ShaderInputConstantIndex m_innerAabbMaxRenderConstantIndex; - RHI::ShaderInputConstantIndex m_useParallaxCorrectionRenderConstantIndex; - RHI::ShaderInputImageIndex m_reflectionCubeMapRenderImageIndex; + RHI::ShaderInputNameIndex m_modelToWorldStencilConstantIndex = "m_modelToWorld"; + RHI::ShaderInputNameIndex m_modelToWorldRenderConstantIndex = "m_modelToWorld"; + RHI::ShaderInputNameIndex m_modelToWorldInverseRenderConstantIndex = "m_modelToWorldInverse"; + RHI::ShaderInputNameIndex m_outerObbHalfLengthsRenderConstantIndex = "m_outerObbHalfLengths"; + RHI::ShaderInputNameIndex m_innerObbHalfLengthsRenderConstantIndex = "m_innerObbHalfLengths"; + RHI::ShaderInputNameIndex m_useParallaxCorrectionRenderConstantIndex = "m_useParallaxCorrection"; + RHI::ShaderInputNameIndex m_reflectionCubeMapRenderImageIndex = "m_reflectionCubeMap"; }; // ReflectionProbe manages all aspects of a single probe, including rendering, visualization, and cubemap generation @@ -78,6 +76,7 @@ namespace AZ void Simulate(uint32_t probeIndex); const Vector3& GetPosition() const { return m_transform.GetTranslation(); } + const AZ::Transform& GetTransform() const { return m_transform; } void SetTransform(const AZ::Transform& transform); const AZ::Vector3& GetOuterExtents() const { return m_outerExtents; } @@ -86,8 +85,8 @@ namespace AZ const AZ::Vector3& GetInnerExtents() const { return m_innerExtents; } void SetInnerExtents(const AZ::Vector3& innerExtents); - const Aabb& GetOuterAabbWs() const { return m_outerAabbWs; } - const Aabb& GetInnerAabbWs() const { return m_innerAabbWs; } + const Obb& GetOuterObbWs() const { return m_outerObbWs; } + const Obb& GetInnerObbWs() const { return m_innerObbWs; } const Data::Instance& GetCubeMapImage() const { return m_cubeMapImage; } void SetCubeMapImage(const Data::Instance& cubeMapImage, const AZStd::string& relativePath); @@ -133,9 +132,9 @@ namespace AZ AZ::Vector3 m_outerExtents = AZ::Vector3(0.0f, 0.0f, 0.0f); AZ::Vector3 m_innerExtents = AZ::Vector3(0.0f, 0.0f, 0.0f); - // probe volume AABBs (world space), built from position and extents - Aabb m_outerAabbWs; - Aabb m_innerAabbWs; + // probe volume OBBs (world space), built from position and extents + Obb m_outerObbWs; + Obb m_innerObbWs; // cubemap Data::Instance m_cubeMapImage; diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp index 91c72013dd..52d089ae0d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -77,61 +76,6 @@ namespace AZ m_reflectionRenderData.m_renderInnerSrgLayout, m_reflectionRenderData.m_renderInnerDrawListTag); - // create ShaderResourceGroups here so we can get the layout and cache the indices - // Note: the SRGs are not needed beyond this method since each probe creates its own SRGs, we are just interested in the indices - - // cache probe stencil shader indices - Data::Instance stencilSrg = RPI::ShaderResourceGroup::Create( - m_reflectionRenderData.m_stencilShader->GetAsset(), - m_reflectionRenderData.m_stencilShader->GetSupervariantIndex(), - m_reflectionRenderData.m_stencilSrgLayout->GetName()); - AZ_Error("ReflectionProbeFeatureProcessor", stencilSrg.get(), "Failed to create stencil back face shader resource group"); - - const RHI::ShaderResourceGroupLayout* stencilSrgLayout = stencilSrg->GetLayout(); - Name modelToWorldConstantName = Name("m_modelToWorld"); - m_reflectionRenderData.m_modelToWorldStencilConstantIndex = stencilSrgLayout->FindShaderInputConstantIndex(modelToWorldConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldStencilConstantIndex.IsValid(), "Failed to find stencil shader input constant [%s]", modelToWorldConstantName.GetCStr()); - - // cache probe render shader indices - // Note: the outer and inner render shaders use the same Srg - Data::Instance renderReflectionSrg = RPI::ShaderResourceGroup::Create( - m_reflectionRenderData.m_renderOuterShader->GetAsset(), - m_reflectionRenderData.m_renderOuterShader->GetSupervariantIndex(), - m_reflectionRenderData.m_renderOuterSrgLayout->GetName()); - AZ_Error("ReflectionProbeFeatureProcessor", renderReflectionSrg.get(), "Failed to create render reflection shader resource group"); - - const RHI::ShaderResourceGroupLayout* renderReflectionSrgLayout = renderReflectionSrg->GetLayout(); - m_reflectionRenderData.m_modelToWorldRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(modelToWorldConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_modelToWorldRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", modelToWorldConstantName.GetCStr()); - - Name aabbPosConstantName = Name("m_aabbPos"); - m_reflectionRenderData.m_aabbPosRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(aabbPosConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_aabbPosRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", aabbPosConstantName.GetCStr()); - - Name outerAabbMinConstantName = Name("m_outerAabbMin"); - m_reflectionRenderData.m_outerAabbMinRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(outerAabbMinConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_outerAabbMinRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", outerAabbMinConstantName.GetCStr()); - - Name outerAabbMaxConstantName = Name("m_outerAabbMax"); - m_reflectionRenderData.m_outerAabbMaxRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(outerAabbMaxConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_outerAabbMaxRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", outerAabbMaxConstantName.GetCStr()); - - Name innerAabbMinConstantName = Name("m_innerAabbMin"); - m_reflectionRenderData.m_innerAabbMinRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(innerAabbMinConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_innerAabbMinRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", innerAabbMinConstantName.GetCStr()); - - Name innerAabbMaxConstantName = Name("m_innerAabbMax"); - m_reflectionRenderData.m_innerAabbMaxRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(innerAabbMaxConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_innerAabbMaxRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", innerAabbMaxConstantName.GetCStr()); - - Name useParallaxCorrectionConstantName = Name("m_useParallaxCorrection"); - m_reflectionRenderData.m_useParallaxCorrectionRenderConstantIndex = renderReflectionSrgLayout->FindShaderInputConstantIndex(useParallaxCorrectionConstantName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_useParallaxCorrectionRenderConstantIndex.IsValid(), "Failed to find render shader input constant [%s]", useParallaxCorrectionConstantName.GetCStr()); - - Name reflectionCubeMapImageName = Name("m_reflectionCubeMap"); - m_reflectionRenderData.m_reflectionCubeMapRenderImageIndex = renderReflectionSrgLayout->FindShaderInputImageIndex(reflectionCubeMapImageName); - AZ_Error("ReflectionProbeFeatureProcessor", m_reflectionRenderData.m_reflectionCubeMapRenderImageIndex.IsValid(), "Failed to find render shader input image [%s]", reflectionCubeMapImageName.GetCStr()); - EnableSceneNotification(); } @@ -197,10 +141,11 @@ namespace AZ // sort the probes by descending inner volume size, so the smallest volumes are rendered last auto sortFn = [](AZStd::shared_ptr const& probe1, AZStd::shared_ptr const& probe2) -> bool { - const Aabb& aabb1 = probe1->GetInnerAabbWs(); - const Aabb& aabb2 = probe2->GetInnerAabbWs(); - float size1 = aabb1.GetXExtent() * aabb1.GetZExtent() * aabb1.GetYExtent(); - float size2 = aabb2.GetXExtent() * aabb2.GetZExtent() * aabb2.GetYExtent(); + const Obb& obb1 = probe1->GetInnerObbWs(); + const Obb& obb2 = probe2->GetInnerObbWs(); + float size1 = obb1.GetHalfLengthX() * obb1.GetHalfLengthZ() * obb1.GetHalfLengthY(); + float size2 = obb2.GetHalfLengthX() * obb2.GetHalfLengthZ() * obb2.GetHalfLengthY(); + return (size1 > size2); }; @@ -345,7 +290,7 @@ namespace AZ // simple AABB check to find the reflection probes that contain the position for (auto& reflectionProbe : m_reflectionProbes) { - if (reflectionProbe->GetOuterAabbWs().Contains(position) + if (reflectionProbe->GetOuterObbWs().Contains(position) && reflectionProbe->GetCubeMapImage() && reflectionProbe->GetCubeMapImage()->IsInitialized()) { diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp index 8fb971f8f5..c0202c7c74 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp index 500eadf110..e0209702dc 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp index d4fa3f52be..ca8775a073 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp @@ -12,7 +12,6 @@ #include -#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp index 6f9c0dc73a..141acbd744 100644 --- a/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp @@ -8,7 +8,6 @@ #include -#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 40188109ca..92f96a591b 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -183,6 +183,8 @@ set(FILES Source/PostProcess/PostProcessFeatureProcessor.h Source/PostProcess/PostProcessSettings.cpp Source/PostProcess/PostProcessSettings.h + Source/PostProcess/ColorGrading/HDRColorGradingSettings.h + Source/PostProcess/ColorGrading/HDRColorGradingSettings.cpp Source/PostProcess/Bloom/BloomSettings.cpp Source/PostProcess/Bloom/BloomSettings.h Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp @@ -205,6 +207,8 @@ set(FILES Source/PostProcessing/BloomCompositePass.cpp Source/PostProcessing/BloomParentPass.h Source/PostProcessing/BloomParentPass.cpp + Source/PostProcessing/HDRColorGradingPass.cpp + Source/PostProcessing/HDRColorGradingPass.h Source/PostProcessing/DepthOfFieldCompositePass.h Source/PostProcessing/DepthOfFieldCompositePass.cpp Source/PostProcessing/DepthOfFieldBokehBlurPass.h diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake index 89febbcf3d..d7af7b0108 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_public_files.cmake @@ -51,6 +51,8 @@ set(FILES Include/Atom/Feature/PostProcess/Bloom/BloomConstants.h Include/Atom/Feature/PostProcess/Bloom/BloomParams.inl Include/Atom/Feature/PostProcess/Bloom/BloomSettingsInterface.h + Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingParams.inl + Include/Atom/Feature/PostProcess/ColorGrading/HDRColorGradingSettingsInterface.h Include/Atom/Feature/PostProcess/DepthOfField/DepthOfFieldConstants.h Include/Atom/Feature/PostProcess/DepthOfField/DepthOfFieldParams.inl Include/Atom/Feature/PostProcess/DepthOfField/DepthOfFieldSettingsInterface.h diff --git a/Gems/Atom/Feature/Common/gem.json b/Gems/Atom/Feature/Common/gem.json index cbaa56e0d7..36a61fbbbb 100644 --- a/Gems/Atom/Feature/Common/gem.json +++ b/Gems/Atom/Feature/Common/gem.json @@ -8,7 +8,12 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI", + "Atom", + "ImGui", + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/MemorySubAllocator.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/MemorySubAllocator.h index bdeb252174..3e464a6974 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/MemorySubAllocator.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/MemorySubAllocator.h @@ -7,7 +7,6 @@ */ #pragma once -#include #include #include #include diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h index 988416326e..7558442ad1 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h @@ -7,8 +7,9 @@ */ #pragma once -#include #include + +#include #include #include #include diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h index 42ba2d2e25..14e9968e42 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/SwapChain.h @@ -81,15 +81,6 @@ namespace AZ AZ_RTTI(SwapChain, "{888B64A5-D956-406F-9C33-CF6A54FC41B0}", Object); -#if defined(PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) - // On Linux platforms that uses XCB, a resize may occur in the swap chain but the command queue may still - // reference the original surface. This flag is a temporary fix to make sure the swap chain is ready to present - // We need to remove this work around with - - // [GFX TODO][GHI - 2678] - AZStd::atomic_bool m_readyToPresent { false }; -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - protected: SwapChain(); diff --git a/Gems/Atom/RHI/Code/Source/RHI/BufferPool.cpp b/Gems/Atom/RHI/Code/Source/RHI/BufferPool.cpp index 5d06d02668..94b0b57c37 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/BufferPool.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/BufferPool.cpp @@ -7,7 +7,6 @@ */ #include -#include #include #include diff --git a/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp b/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp index 0983d4db22..b50c36d3f9 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp @@ -7,9 +7,10 @@ */ #include -#include #include +#include + namespace AZ { namespace RHI diff --git a/Gems/Atom/RHI/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/Code/Source/RHI/Device.cpp index 9453ff79ee..2f4ca297a1 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/Device.cpp @@ -6,7 +6,6 @@ * */ -#include #include #include diff --git a/Gems/Atom/RHI/Code/Source/RHI/Fence.cpp b/Gems/Atom/RHI/Code/Source/RHI/Fence.cpp index ca0493a52c..d030ff8d2b 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/Fence.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/Fence.cpp @@ -8,6 +8,8 @@ #include +#include + namespace AZ { namespace RHI diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp index b85fa88f34..9f3d21a2f1 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameGraphCompiler.cpp index 5c5691d975..d06ec002e4 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameGraphCompiler.cpp @@ -6,7 +6,6 @@ * */ -#include #include #include #include diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameGraphExecuter.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameGraphExecuter.cpp index 429081cd2c..6888531b67 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameGraphExecuter.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameGraphExecuter.cpp @@ -6,7 +6,6 @@ * */ #include -#include #include #include #include diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp index 90218709fa..df887379fe 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp @@ -6,7 +6,6 @@ * */ -#include #include #include #include diff --git a/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp b/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp index 868580f0c7..0c06887dd6 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp @@ -6,9 +6,10 @@ * */ -#include #include #include + +#include #include #include diff --git a/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp b/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp index 69f7002435..ad3ab119ef 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp @@ -6,12 +6,12 @@ * */ -#include #include #include #include #include +#include #include #include diff --git a/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp index 92d7a125d8..b0501d937d 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/SwapChain.cpp @@ -164,12 +164,6 @@ namespace AZ m_currentImageIndex = 0; } -#if defined(PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) - // If we are presenting through the editor, the resize is triggered through the editor's window, which - // won't happen until after the surface is ready to present - m_readyToPresent.store(true); -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - return resultCode; } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListPool.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListPool.cpp index 94262064da..20021e71ba 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListPool.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListPool.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include namespace AZ diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp index eee137026d..9bf25d8bc6 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp @@ -5,7 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include + #include #include #include diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp index 816b904e16..635c66f1cd 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include namespace AZ diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp index 722d535cb1..4ff7d581a4 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/Gems/Atom/RHI/DX12/gem.json b/Gems/Atom/RHI/DX12/gem.json index df0ae12cc4..eb876a1b9f 100644 --- a/Gems/Atom/RHI/DX12/gem.json +++ b/Gems/Atom/RHI/DX12/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp index fb11700e04..e4c651c2b1 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp @@ -7,7 +7,6 @@ */ #include -#include #include #include #include diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/FrameGraphCompiler.cpp index a347ed9b12..9ee000e166 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/FrameGraphCompiler.cpp @@ -5,7 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include + #include #include #include diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/Scope.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/Scope.cpp index 7e4d510dfa..c680684efd 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/Scope.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/Scope.cpp @@ -5,7 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#include + #include #include #include diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp index 567f89b6e0..d05e1a00a3 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp @@ -6,10 +6,10 @@ * */ +#include #include #include #include -#include #include #include #include diff --git a/Gems/Atom/RHI/Metal/gem.json b/Gems/Atom/RHI/Metal/gem.json index 497e0149b6..8da6bfabec 100644 --- a/Gems/Atom/RHI/Metal/gem.json +++ b/Gems/Atom/RHI/Metal/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/Null/gem.json b/Gems/Atom/RHI/Null/gem.json index aa2e08501a..0870efaae7 100644 --- a/Gems/Atom/RHI/Null/gem.json +++ b/Gems/Atom/RHI/Null/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp index e4d8212228..c712099172 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueue.cpp @@ -42,15 +42,6 @@ namespace AZ void CommandQueue::ExecuteWork(const RHI::ExecuteWorkRequest& rhiRequest) { -#if defined(PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) - for (RHI::SwapChain* swapChain : rhiRequest.m_swapChainsToPresent) - { - if (!swapChain->m_readyToPresent) - { - return; - } - } -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB const ExecuteWorkRequest& request = static_cast(rhiRequest); QueueCommand([=](void* queue) { diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp index c0d34e2bf9..ec7311cd6e 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphCompiler.cpp index 2a30e36e04..e153ad6645 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphCompiler.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp index e1d3e8c060..7040defca8 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp @@ -128,17 +128,6 @@ namespace AZ nativeDimensions->m_imageFormat = ConvertFormat(m_surfaceFormat.format); } -#if defined(PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) - // When launching in game mode, the surface will be ready at this point, meaning that after - // intialization, this swap chain is ready to present - AZ::ApplicationTypeQuery appType; - ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType); - if (appType.IsGame()) - { - m_readyToPresent.store(true); - } -#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - SetName(GetName()); return result; } diff --git a/Gems/Atom/RHI/Vulkan/gem.json b/Gems/Atom/RHI/Vulkan/gem.json index 2b43de4ab1..81e8dd22ea 100644 --- a/Gems/Atom/RHI/Vulkan/gem.json +++ b/Gems/Atom/RHI/Vulkan/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/RHI/gem.json b/Gems/Atom/RHI/gem.json index 015ee64994..5f916e5224 100644 --- a/Gems/Atom/RHI/gem.json +++ b/Gems/Atom/RHI/gem.json @@ -8,7 +8,13 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI_DX12", + "Atom_RHI_Metal", + "Atom_RHI_Vulkan", + "Atom_RHI_Null", + "Atom_Feature_Common" + ] } diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli index a94e01e11b..b004623c4e 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/Math.azsli @@ -116,6 +116,22 @@ float ComputeLerpBetweenInnerOuterAABBs(float3 innerAabbMin, float3 innerAabbMax return totalDistance > 0.0f ? saturate(shortestDistance / totalDistance) : 1.0f; } +// returns true if the Obb contains the specified point +bool ObbContainsPoint(float4x4 obbTransformInverse, float3 obbHalfExtents, float3 testPoint) +{ + // get the position in Obb local space, force to positive quadrant with abs() + float4 p = abs(mul(obbTransformInverse, float4(testPoint, 1.0f))); + return AabbContainsPoint(-obbHalfExtents, obbHalfExtents, p); +} + +// computes [0..1] percentage of a point that's in between the inner and outer OBBs +float ComputeLerpBetweenInnerOuterOBBs(float3x4 obbTransformInverse, float3 innerObbHalfExtents, float3 outerObbHalfExtents, float3 position) +{ + // get the position in Obb local space, force to positive quadrant with abs() + float3 p = abs(mul(obbTransformInverse, float4(position, 1.0f))); + return ComputeLerpBetweenInnerOuterAABBs(-innerObbHalfExtents, innerObbHalfExtents, outerObbHalfExtents, float3(0.0f, 0.0f, 0.0f), p); +} + // ---------- Normal Encoding ----------- // Encode/Decode functions for Signed Octahedron normals diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp index 5ddeb08ef8..ef0678046c 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialBuilder.cpp @@ -30,6 +30,7 @@ #include #include #include +#include namespace AZ { @@ -46,7 +47,7 @@ namespace AZ { AssetBuilderSDK::AssetBuilderDesc materialBuilderDescriptor; materialBuilderDescriptor.m_name = JobKey; - materialBuilderDescriptor.m_version = 107; // ATOM-14918 + materialBuilderDescriptor.m_version = 108; // Set materialtype dependency to OrderOnce materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.material", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.materialtype", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); materialBuilderDescriptor.m_busId = azrtti_typeid(); @@ -66,21 +67,19 @@ namespace AZ //! Adds all relevant dependencies for a referenced source file, considering that the path might be relative to the original file location or a full asset path. //! This will usually include multiple source dependencies and a single job dependency, but will include only source dependencies if the file is not found. //! Note the AssetBuilderSDK::JobDependency::m_platformIdentifier will not be set by this function. The calling code must set this value before passing back - //! to the AssetBuilderSDK::CreateJobsResponse. - void AddPossibleDependencies( - AZStd::string_view currentFilePath, AZStd::string_view referencedParentPath, - AZStd::vector& sourceFileDependencies, - const char* jobKey, AZStd::vector& jobDependencies) + //! to the AssetBuilderSDK::CreateJobsResponse. If isOrderedOnceForMaterialTypes is true and the dependency is a materialtype file, the job dependency type + //! will be set to JobDependencyType::OrderOnce. + void AddPossibleDependencies(AZStd::string_view currentFilePath, + AZStd::string_view referencedParentPath, + const char* jobKey, + AZStd::vector& jobDependencies, + bool isOrderedOnceForMaterialTypes = false) { bool dependencyFileFound = false; AZStd::vector possibleDependencies = RPI::AssetUtils::GetPossibleDepenencyPaths(currentFilePath, referencedParentPath); for (auto& file : possibleDependencies) { - AssetBuilderSDK::SourceFileDependency sourceFileDependency; - sourceFileDependency.m_sourceFileDependencyPath = file; - sourceFileDependencies.push_back(sourceFileDependency); - // The first path found is the highest priority, and will have a job dependency, as this is the one // the builder will actually use if (!dependencyFileFound) @@ -93,8 +92,11 @@ namespace AZ { AssetBuilderSDK::JobDependency jobDependency; jobDependency.m_jobKey = jobKey; - jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order; jobDependency.m_sourceFile.m_sourceFileDependencyPath = file; + + const bool isMaterialTypeFile = AzFramework::StringFunc::Path::IsExtension(file.c_str(), MaterialTypeSourceData::Extension); + jobDependency.m_type = (isMaterialTypeFile && isOrderedOnceForMaterialTypes) ? AssetBuilderSDK::JobDependencyType::OrderOnce : AssetBuilderSDK::JobDependencyType::Order; + jobDependencies.push_back(jobDependency); } } @@ -173,8 +175,9 @@ namespace AZ for (auto& shader : materialTypeSourceData.GetValue().m_shaderCollection) { - AddPossibleDependencies(request.m_sourceFile, shader.m_shaderFilePath, - response.m_sourceFileDependencyList, "Shader Asset", + AddPossibleDependencies(request.m_sourceFile, + shader.m_shaderFilePath, + "Shader Asset", outputJobDescriptor.m_jobDependencyList); } @@ -184,9 +187,10 @@ namespace AZ for (const MaterialFunctorSourceData::AssetDependency& dependency : dependencies) { - AddPossibleDependencies(request.m_sourceFile, dependency.m_sourceFilePath, - response.m_sourceFileDependencyList, - dependency.m_jobKey.c_str(), outputJobDescriptor.m_jobDependencyList); + AddPossibleDependencies(request.m_sourceFile, + dependency.m_sourceFilePath, + dependency.m_jobKey.c_str(), + outputJobDescriptor.m_jobDependencyList); } } } @@ -219,11 +223,24 @@ namespace AZ parentMaterialPath = materialTypePath; } + // If includeMaterialPropertyNames is false, then a job dependency is needed so the material builder can validate MaterialAsset properties + // against the MaterialTypeAsset at asset build time. + // If includeMaterialPropertyNames is true, the material properties will be validated at runtime when the material is loaded, so the job dependency + // is needed only for first-time processing to set up the initial MaterialAsset. This speeds up AP processing time when a materialtype file + // is edited (e.g. 10s when editing StandardPBR.materialtype on AtomTest project from 45s). + bool includeMaterialPropertyNames = true; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Get(includeMaterialPropertyNames, "/O3DE/Atom/RPI/MaterialBuilder/IncludeMaterialPropertyNames"); + } + // Register dependency on the parent material source file so we can load it and use it's data to build this variant material. // Note, we don't need a direct dependency on the material type because the parent material will depend on it. - AddPossibleDependencies(request.m_sourceFile, parentMaterialPath, - response.m_sourceFileDependencyList, - JobKey, outputJobDescriptor.m_jobDependencyList); + AddPossibleDependencies(request.m_sourceFile, + parentMaterialPath, + JobKey, + outputJobDescriptor.m_jobDependencyList, + includeMaterialPropertyNames); } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp index cf7e53c596..c83761cf8e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MaterialAssetBuilderComponent.cpp @@ -44,7 +44,7 @@ namespace AZ if (auto* serialize = azrtti_cast(context)) { serialize->Class() - ->Version(4) + ->Version(5) // Set materialtype dependency to OrderOnce ->Attribute(Edit::Attributes::SystemComponentTags, AZStd::vector({ AssetBuilderSDK::ComponentTags::AssetBuilder })); } } @@ -78,10 +78,7 @@ namespace AZ AZStd::string materialTypePath; RPI::MaterialConverterBus::BroadcastResult(materialTypePath, &RPI::MaterialConverterBus::Events::GetMaterialTypePath); - bool includeMaterialPropertyNames = true; - RPI::MaterialConverterBus::BroadcastResult(includeMaterialPropertyNames, &RPI::MaterialConverterBus::Events::ShouldIncludeMaterialPropertyNames); - // TODO: Use includeMaterialPropertyNames to break materialtype dependency on fbx files. Materialasset's dependency on materialtypeasset will need to be decoupled first - if (conversionEnabled && !materialTypePath.empty() /*&& !includeMaterialPropertyNames*/) + if (conversionEnabled && !materialTypePath.empty()) { AssetBuilderSDK::SourceFileDependency materialTypeSource; materialTypeSource.m_sourceFileDependencyPath = materialTypePath; @@ -90,7 +87,15 @@ namespace AZ jobDependency.m_jobKey = "Atom Material Builder"; jobDependency.m_sourceFile = materialTypeSource; jobDependency.m_platformIdentifier = platformIdentifier; - jobDependency.m_type = AssetBuilderSDK::JobDependencyType::Order; + + // If includeMaterialPropertyNames is false, then a job dependency is needed so the material builder can validate + // MaterialAsset properties against the MaterialTypeAsset at asset build time. If includeMaterialPropertyNames is true, the + // material properties will be validated at runtime when the material is loaded, so the job dependency is needed only for + // first-time processing to set up the initial MaterialAsset. This speeds up AP processing time when a materialtype file is + // edited (e.g. 10s when editing StandardPBR.materialtype on AtomTest project from 45s). + bool includeMaterialPropertyNames = true; + RPI::MaterialConverterBus::BroadcastResult(includeMaterialPropertyNames, &RPI::MaterialConverterBus::Events::ShouldIncludeMaterialPropertyNames); + jobDependency.m_type = includeMaterialPropertyNames ? AssetBuilderSDK::JobDependencyType::OrderOnce : AssetBuilderSDK::JobDependencyType::Order; jobDependencyList.push_back(jobDependency); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index f67343faa1..348f0aa57a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -14,8 +14,6 @@ #include #include -#include - #include #include #include @@ -730,12 +728,22 @@ namespace AZ m_debugCtx.ResetCullStats(); m_debugCtx.m_numCullablesInScene = GetNumCullables(); + AZ::JobCompletion beginCullingCompletion; for (auto& view : views) { - view->BeginCulling(); + const auto cullingLambda = [&view]() + { + view->BeginCulling(); + }; + + AZ::Job* cullingJob = AZ::CreateJobFunction(AZStd::move(cullingLambda), true, nullptr); + cullingJob->SetDependent(&beginCullingCompletion); + cullingJob->Start(); } + beginCullingCompletion.StartAndWaitForCompletion(); + AuxGeomDrawPtr auxGeom; if (m_debugCtx.m_debugDraw) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQuerySystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQuerySystem.cpp index 8077038ea7..0143c8ee2d 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQuerySystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQuerySystem.cpp @@ -7,7 +7,6 @@ */ #include -#include #include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Image/ImageSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Image/ImageSystem.cpp index 71e2eaa2f5..f201e8414c 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Image/ImageSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Image/ImageSystem.cpp @@ -23,7 +23,6 @@ #include #include -#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp index 34d72338dc..f4f51f97b7 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp @@ -19,7 +19,6 @@ #include -#include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 778548f14c..a7ecf089c0 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -6,8 +6,6 @@ * */ -#include - #include #include #include diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp index bb80a1b8d2..feeeff36cf 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Buffer/BufferAssetCreator.cpp @@ -57,7 +57,7 @@ namespace AZ // Only allocate buffer if initial data is not empty if (initialData != nullptr && initialDataSize > 0) { - bufferAsset->m_buffer.resize(descriptor.m_byteCount); + bufferAsset->m_buffer.resize_no_construct(descriptor.m_byteCount); memcpy(bufferAsset->m_buffer.data(), initialData, initialDataSize); } diff --git a/Gems/Atom/RPI/gem.json b/Gems/Atom/RPI/gem.json index f30a1c3201..b5a6fd5a1a 100644 --- a/Gems/Atom/RPI/gem.json +++ b/Gems/Atom/RPI/gem.json @@ -8,7 +8,9 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI" + ] } diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h index 42fe9a01c9..4956fbc1dc 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h @@ -116,11 +116,18 @@ namespace AtomToolsFramework // ModularViewportCameraControllerRequestBus overrides ... void InterpolateToTransform(const AZ::Transform& worldFromLocal, float lookAtDistance) override; AZStd::optional LookAtAfterInterpolation() const override; + AZ::Transform GetReferenceFrame() const override; + void SetReferenceFrame(const AZ::Transform& worldFromLocal) override; + void ClearReferenceFrame() override; private: // AzFramework::ViewportDebugDisplayEventBus overrides ... void DisplayViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; + //! Update the reference frame after a change has been made to the camera + //! view without updating the internal camera via user input. + void RefreshReferenceFrame(); + //! The current mode the camera controller is in. enum class CameraMode { @@ -139,6 +146,8 @@ namespace AtomToolsFramework AzFramework::Camera m_camera; //!< The current camera state (pitch/yaw/position/look-distance). AzFramework::Camera m_targetCamera; //!< The target (next) camera state that m_camera is catching up to. + AzFramework::Camera m_previousCamera; //!< The state of the camera from the previous frame. + AZStd::optional m_storedCamera; //!< A potentially stored camera for when a custom reference frame is set. AzFramework::CameraSystem m_cameraSystem; //!< The camera system responsible for managing all CameraInputs. AzFramework::CameraProps m_cameraProps; //!< Camera properties to control rotate and translate smoothness. CameraControllerPriorityFn m_priorityFn; //!< Controls at what priority the camera controller should respond to events. @@ -147,6 +156,7 @@ namespace AtomToolsFramework CameraMode m_cameraMode = CameraMode::Control; //!< The current mode the camera is operating in. AZStd::optional m_lookAtAfterInterpolation; //!< The look at point after an interpolation has finished. //!< Will be cleared when the view changes (camera looks away). + AZ::Transform m_referenceFrameOverride = AZ::Transform::CreateIdentity(); //!< //! Flag to prevent circular updates of the camera transform (while the viewport transform is being updated internally). bool m_updatingTransformInternally = false; //! Listen for camera view changes outside of the camera controller. @@ -154,4 +164,17 @@ namespace AtomToolsFramework //! The current instance of the modular camera viewport context. AZStd::unique_ptr m_modularCameraViewportContext; }; + + //! Placeholder implementation for ModularCameraViewportContext (useful for verifying the interface). + class PlaceholderModularCameraViewportContextImpl : public AtomToolsFramework::ModularCameraViewportContext + { + public: + AZ::Transform GetCameraTransform() const override; + void SetCameraTransform(const AZ::Transform& transform) override; + void ConnectViewMatrixChangedHandler(AZ::RPI::ViewportContext::MatrixChangedEvent::Handler& handler) override; + + private: + AZ::Transform m_cameraTransform = AZ::Transform::CreateIdentity(); + AZ::RPI::ViewportContext::MatrixChangedEvent m_viewMatrixChangedEvent; + }; } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h index ae4dc5dd25..388d24164a 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h @@ -35,6 +35,16 @@ namespace AtomToolsFramework //! Look at point after an interpolation has finished and no translation has occurred. virtual AZStd::optional LookAtAfterInterpolation() const = 0; + //! Return the current reference frame. + //! @note If a reference frame has not been set or a frame has been cleared, this is just the identity. + virtual AZ::Transform GetReferenceFrame() const = 0; + + //! Set a new reference frame other than the identity for the camera controller. + virtual void SetReferenceFrame(const AZ::Transform& worldFromLocal) = 0; + + //! Clear the current reference frame to restore the identity. + virtual void ClearReferenceFrame() = 0; + protected: ~ModularViewportCameraControllerRequests() = default; }; 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 1b718f7710..318a49027a 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -159,8 +159,6 @@ namespace AtomToolsFramework AZ::RPI::ViewPtr m_defaultCamera; // Our viewport-local aux geom pipeline for supplemental rendering. AZ::RPI::AuxGeomDrawPtr m_auxGeom; - // Used to keep track of a pending resize event to avoid initialization before window activate. - bool m_windowResizedEvent = false; // Tracks whether the cursor is currently over our viewport, used for mouse input event book-keeping. bool m_mouseOver = false; // The last recorded mouse position, in local viewport screen coordinates. diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index 36e45bb7ca..13b9a2ba27 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -87,6 +87,7 @@ namespace AtomToolsFramework AtomToolsApplication ::~AtomToolsApplication() { + m_styleManager.reset(); AtomToolsMainWindowNotificationBus::Handler::BusDisconnect(); AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect(); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect(); @@ -174,12 +175,14 @@ namespace AtomToolsFramework AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@assets@/assetcatalog.xml"); - AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets(); + if (!AZ::RPI::RPISystemInterface::Get()->IsInitialized()) + { + AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets(); + } LoadSettings(); AtomToolsMainWindowNotificationBus::Handler::BusConnect(); - AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::CreateMainWindow); auto editorPythonEventsInterface = AZ::Interface::Get(); @@ -206,6 +209,7 @@ namespace AtomToolsFramework { // before modules are unloaded, destroy UI to free up any assets it cached AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::DestroyMainWindow); + m_styleManager.reset(); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect(); AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect(); @@ -461,6 +465,7 @@ namespace AtomToolsFramework void AtomToolsApplication::Stop() { AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::DestroyMainWindow); + m_styleManager.reset(); UnloadSettings(); Base::Stop(); @@ -468,7 +473,7 @@ namespace AtomToolsFramework void AtomToolsApplication::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const { - appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Game; + appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Tool; } void AtomToolsApplication::OnTraceMessage([[maybe_unused]] AZStd::string_view message) diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp index 4419e0c49f..bcc9a08f77 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp @@ -30,6 +30,18 @@ namespace AtomToolsFramework ""); AZ_CVAR(float, ed_cameraSystemOrbitPointSize, 0.1f, nullptr, AZ::ConsoleFunctorFlags::Null, ""); + AZ::Transform TransformFromMatrix4x4(const AZ::Matrix4x4& matrix) + { + const auto rotation = AZ::Matrix3x3::CreateFromMatrix4x4(matrix); + const auto translation = matrix.GetTranslation(); + return AZ::Transform::CreateFromMatrix3x3AndTranslation(rotation, translation); + } + + AZ::Matrix4x4 Matrix4x4FromTransform(const AZ::Transform& transform) + { + return AZ::Matrix4x4::CreateFromQuaternionAndTranslation(transform.GetRotation(), transform.GetTranslation()); + } + // debug void DrawPreviewAxis(AzFramework::DebugDisplayRequests& display, const AZ::Transform& transform, const float axisLength) { @@ -167,11 +179,19 @@ namespace AtomToolsFramework controller->SetupCameraControllerPriority(m_priorityFn); controller->SetupCameraControllerViewportContext(m_modularCameraViewportContext); - auto handleCameraChange = [this](const AZ::Matrix4x4&) + auto handleCameraChange = [this]([[maybe_unused]] const AZ::Matrix4x4& cameraView) { // ignore these updates if the camera is being updated internally if (!m_updatingTransformInternally) { + if (m_storedCamera.has_value()) + { + // if an external change occurs ensure we update the stored reference frame if one is set + RefreshReferenceFrame(); + return; + } + + m_previousCamera = m_targetCamera; UpdateCameraFromTransform(m_targetCamera, m_modularCameraViewportContext->GetCameraTransform()); m_camera = m_targetCamera; } @@ -231,7 +251,7 @@ namespace AtomToolsFramework } } - m_modularCameraViewportContext->SetCameraTransform(m_camera.Transform()); + m_modularCameraViewportContext->SetCameraTransform(m_referenceFrameOverride * m_camera.Transform()); } else if (m_cameraMode == CameraMode::Animation) { @@ -240,6 +260,8 @@ namespace AtomToolsFramework return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); }; + m_cameraAnimation.m_time = AZ::GetClamp(m_cameraAnimation.m_time + event.m_deltaTime.count(), 0.0f, 1.0f); + const auto& [transformStart, transformEnd, animationTime] = m_cameraAnimation; const float transitionTime = smootherStepFn(animationTime); @@ -253,14 +275,13 @@ namespace AtomToolsFramework m_camera.m_lookAt = current.GetTranslation(); m_targetCamera = m_camera; + m_modularCameraViewportContext->SetCameraTransform(current); + if (animationTime >= 1.0f) { m_cameraMode = CameraMode::Control; + RefreshReferenceFrame(); } - - m_cameraAnimation.m_time = AZ::GetClamp(animationTime + event.m_deltaTime.count(), 0.0f, 1.0f); - - m_modularCameraViewportContext->SetCameraTransform(current); } m_updatingTransformInternally = false; @@ -280,7 +301,7 @@ namespace AtomToolsFramework void ModularViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal, const float lookAtDistance) { m_cameraMode = CameraMode::Animation; - m_cameraAnimation = CameraAnimation{ m_camera.Transform(), worldFromLocal, 0.0f }; + m_cameraAnimation = CameraAnimation{ m_referenceFrameOverride * m_camera.Transform(), worldFromLocal, 0.0f }; m_lookAtAfterInterpolation = worldFromLocal.GetTranslation() + worldFromLocal.GetBasisY() * lookAtDistance; } @@ -288,4 +309,59 @@ namespace AtomToolsFramework { return m_lookAtAfterInterpolation; } + + AZ::Transform ModularViewportCameraControllerInstance::GetReferenceFrame() const + { + return m_referenceFrameOverride; + } + + void ModularViewportCameraControllerInstance::SetReferenceFrame(const AZ::Transform& worldFromLocal) + { + if (!m_storedCamera.has_value()) + { + m_storedCamera = m_previousCamera; + } + + m_referenceFrameOverride = worldFromLocal; + m_targetCamera.m_pitch = 0.0f; + m_targetCamera.m_yaw = 0.0f; + m_targetCamera.m_lookAt = AZ::Vector3::CreateZero(); + m_targetCamera.m_lookDist = 0.0f; + m_camera = m_targetCamera; + } + + void ModularViewportCameraControllerInstance::ClearReferenceFrame() + { + m_referenceFrameOverride = AZ::Transform::CreateIdentity(); + + if (m_storedCamera.has_value()) + { + m_targetCamera = m_storedCamera.value(); + m_camera = m_targetCamera; + } + + m_storedCamera.reset(); + } + + void ModularViewportCameraControllerInstance::RefreshReferenceFrame() + { + m_referenceFrameOverride = m_modularCameraViewportContext->GetCameraTransform() * m_camera.Transform().GetInverse(); + } + + AZ::Transform PlaceholderModularCameraViewportContextImpl::GetCameraTransform() const + { + return m_cameraTransform; + } + + void PlaceholderModularCameraViewportContextImpl::SetCameraTransform(const AZ::Transform& transform) + { + m_cameraTransform = transform; + m_viewMatrixChangedEvent.Signal(AzFramework::CameraViewFromCameraTransform(Matrix4x4FromTransform(transform))); + } + + void PlaceholderModularCameraViewportContextImpl::ConnectViewMatrixChangedHandler( + AZ::RPI::ViewportContext::MatrixChangedEvent::Handler& handler) + { + handler.Connect(m_viewMatrixChangedEvent); + } } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index 4446e11231..2a15041e20 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -254,28 +254,11 @@ namespace AtomToolsFramework void RenderViewportWidget::resizeEvent([[maybe_unused]] QResizeEvent* event) { - // We need to wait until the window is activated, so the underlying surface - // has been created and has the correct size. - if (windowHandle()->isActive()) - { - SendWindowResizeEvent(); - } - else - { - m_windowResizedEvent = true; - } + SendWindowResizeEvent(); } bool RenderViewportWidget::event(QEvent* event) { - // Check if we have a pending resize event. - // At this point the surface has been created and has - // the proper dimensions. - if (event->type() == QEvent::WindowActivate && m_windowResizedEvent) - { - SendWindowResizeEvent(); - } - return QWidget::event(event); } @@ -372,7 +355,6 @@ namespace AtomToolsFramework AzFramework::WindowNotificationBus::Event( GetNativeWindowHandle(), &AzFramework::WindowNotifications::OnWindowResized, windowSize.width(), windowSize.height()); - m_windowResizedEvent = false; } void RenderViewportWidget::NotifyUpdateRefreshRate() diff --git a/Gems/Atom/Tools/AtomToolsFramework/gem.json b/Gems/Atom/Tools/AtomToolsFramework/gem.json index 5cea71c5cc..2b0380bdae 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/gem.json +++ b/Gems/Atom/Tools/AtomToolsFramework/gem.json @@ -8,7 +8,11 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI", + "Atom_RHI", + "Atom_Bootstrap" + ] } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt b/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt index 6230217ac2..d585e81162 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt +++ b/Gems/Atom/Tools/MaterialEditor/Code/CMakeLists.txt @@ -93,12 +93,14 @@ ly_add_target( AUTOMOC FILES_CMAKE materialeditor_files.cmake - Source/Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + PLATFORM_INCLUDE_FILES + ${pal_source_dir}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake INCLUDE_DIRECTORIES PRIVATE . Source - Source/Platform/${PAL_PLATFORM_NAME} + ${pal_source_dir} PUBLIC Include BUILD_DEPENDENCIES @@ -108,8 +110,14 @@ ly_add_target( Gem::MaterialEditor.Window Gem::MaterialEditor.Viewport Gem::MaterialEditor.Document + RUNTIME_DEPENDENCIES + Gem::AtomToolsFramework.Editor + Gem::EditorPythonBindings.Editor + Gem::ImageProcessingAtom.Editor ) +ly_set_gem_variant_to_load(TARGETS MaterialEditor VARIANTS Tools) + # Add a 'builders' alias to allow the MaterialEditor root gem path to be added to the generated # cmake_dependencies..assetprocessor.setreg to allow the asset scan folder for it to be added ly_create_alias(NAME MaterialEditor.Builders NAMESPACE Gem) @@ -118,26 +126,6 @@ ly_create_alias(NAME MaterialEditor.Builders NAMESPACE Gem) # Editor opens up the MaterialEditor ly_add_dependencies(Editor Gem::MaterialEditor) -ly_add_target_files( - TARGETS - MaterialEditor - FILES - ${CMAKE_CURRENT_LIST_DIR}/../MaterialEditor.xml - OUTPUT_SUBDIRECTORY - Gems/Atom/Tools/MaterialEditor -) - -ly_add_target_dependencies( - TARGETS - MaterialEditor - DEPENDENCIES_FILES - tool_dependencies.cmake - Source/Platform/${PAL_PLATFORM_NAME}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake - # The Material Editor needs the LyShine "Tools" gem variant for the custom LyShine pass - DEPENDENT_TARGETS - Gem::LyShine.Tools -) - # Inject the project path into the MaterialEditor VS debugger command arguments if the build system being invoked # in a project centric view if(NOT PROJECT_NAME STREQUAL "O3DE") diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp deleted file mode 100644 index 14b5fee1fe..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/MaterialEditor_Linux.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include - -namespace Platform -{ - void LoadPluginDependencies() - { - AZ_Warning("Material Editor", false, "LoadPluginDependencies() function is not implemented"); - } - - void ProcessInput(void* message) - { - AZ_Warning("Material Editor", false, "ProcessInput() function is not implemented"); - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - AZ_Warning("Material Editor", false, "GetWindowHandle() function is not implemented"); - AZ_UNUSED(winId); - return nullptr; - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - AZ_Warning("Material Editor", false, "GetClientAreaSize() function is not implemented"); - AZ_UNUSED(window); - return AzFramework::WindowSize{1,1}; - } -} diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake index c09abb0dd2..2417ad1b55 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/platform_linux_files.cmake @@ -9,5 +9,4 @@ set(FILES MaterialEditor_Traits_Platform.h MaterialEditor_Traits_Linux.h - MaterialEditor_Linux.cpp ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/tool_dependencies_linux.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/tool_dependencies_linux.cmake index 5bf4d7cb7e..b2885100e9 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/tool_dependencies_linux.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Linux/tool_dependencies_linux.cmake @@ -6,5 +6,5 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Mac.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Mac.cpp deleted file mode 100644 index 14b5fee1fe..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/MaterialEditor_Mac.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include - -namespace Platform -{ - void LoadPluginDependencies() - { - AZ_Warning("Material Editor", false, "LoadPluginDependencies() function is not implemented"); - } - - void ProcessInput(void* message) - { - AZ_Warning("Material Editor", false, "ProcessInput() function is not implemented"); - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - AZ_Warning("Material Editor", false, "GetWindowHandle() function is not implemented"); - AZ_UNUSED(winId); - return nullptr; - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - AZ_Warning("Material Editor", false, "GetClientAreaSize() function is not implemented"); - AZ_UNUSED(window); - return AzFramework::WindowSize{1,1}; - } -} diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/platform_mac_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/platform_mac_files.cmake index ec56cc1c4e..7275f82047 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/platform_mac_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/platform_mac_files.cmake @@ -9,5 +9,4 @@ set(FILES MaterialEditor_Traits_Platform.h MaterialEditor_Traits_Mac.h - MaterialEditor_Mac.cpp ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/tool_dependencies_mac.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/tool_dependencies_mac.cmake index 5bf4d7cb7e..b2885100e9 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/tool_dependencies_mac.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Mac/tool_dependencies_mac.cmake @@ -6,5 +6,5 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Windows.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Windows.cpp deleted file mode 100644 index 65c135315b..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/MaterialEditor_Windows.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include -#include -#include - -namespace Platform -{ - void ProcessInput(void* message) - { - MSG* msg = (MSG*)message; - - // Ensure that the Windows WM_INPUT messages get passed through to the AzFramework input system, - // but only while in game mode so we don't accumulate raw input events before we start actually - // ticking the input devices, otherwise the queued events will get sent when entering game mode. - if (msg->message == WM_INPUT) - { - UINT rawInputSize; - const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER); - GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, NULL, &rawInputSize, rawInputHeaderSize); - - LPBYTE rawInputBytes = new BYTE[rawInputSize]; - GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize); - - RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes; - - AzFramework::RawInputNotificationBusWindows::Broadcast( - &AzFramework::RawInputNotificationBusWindows::Events::OnRawInputEvent, *rawInput); - } - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - return reinterpret_cast(winId); - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - RECT r; - if (GetWindowRect(reinterpret_cast(window), &r)) - { - return AzFramework::WindowSize{aznumeric_cast(r.right - r.left), aznumeric_cast(r.bottom - r.top)}; - } - else - { - AZ_Assert(false, "Failed to get dimensions for window"); - return AzFramework::WindowSize{}; - } - } -} diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/platform_windows_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/platform_windows_files.cmake index c104a2b8be..608cb36a28 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/platform_windows_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/platform_windows_files.cmake @@ -9,6 +9,5 @@ set(FILES MaterialEditor_Traits_Platform.h MaterialEditor_Traits_Windows.h - MaterialEditor_Windows.cpp MaterialEditor.rc ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/tool_dependencies_windows.cmake b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/tool_dependencies_windows.cmake index 374438983f..e1e811ff67 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/tool_dependencies_windows.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Platform/Windows/tool_dependencies_windows.cmake @@ -6,6 +6,6 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES Gem::QtForPython.Editor ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp index 36498d6d08..7edff11174 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp @@ -231,12 +231,10 @@ namespace MaterialEditor MaterialViewportNotificationBus::Handler::BusConnect(); AZ::TickBus::Handler::BusConnect(); AZ::TransformNotificationBus::MultiHandler::BusConnect(m_cameraEntity->GetId()); - AzFramework::WindowSystemRequestBus::Handler::BusConnect(); } MaterialViewportRenderer::~MaterialViewportRenderer() { - AzFramework::WindowSystemRequestBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::MultiHandler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect(); AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect(); @@ -287,11 +285,6 @@ namespace MaterialEditor return m_viewportController; } - AzFramework::NativeWindowHandle MaterialViewportRenderer::GetDefaultWindowHandle() - { - return (m_windowContext) ? m_windowContext->GetWindowHandle() : nullptr; - } - void MaterialViewportRenderer::OnDocumentOpened(const AZ::Uuid& documentId) { AZ::Data::Instance materialInstance; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h index 8a3e0ca4ff..c6380ddc00 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.h @@ -16,7 +16,6 @@ #include #include #include -#include #include namespace AZ @@ -46,7 +45,6 @@ namespace MaterialEditor , public AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler , public MaterialViewportNotificationBus::Handler , public AZ::TransformNotificationBus::MultiHandler - , public AzFramework::WindowSystemRequestBus::Handler { public: AZ_CLASS_ALLOCATOR(MaterialViewportRenderer, AZ::SystemAllocator, 0); @@ -81,9 +79,6 @@ namespace MaterialEditor // AZ::TransformNotificationBus::MultiHandler overrides... void OnTransformChanged(const AZ::Transform&, const AZ::Transform&) override; - // AzFramework::WindowSystemRequestBus::Handler overrides ... - AzFramework::NativeWindowHandle GetDefaultWindowHandle() override; - using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle; AZ::Data::Instance m_swapChainPass; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp index c78951ed45..0322fa194e 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp @@ -13,24 +13,16 @@ #include #include -#include -#include +#include +#include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include #include -#include "Source/Viewport/ui_MaterialViewportWidget.h" +#include "Viewport/ui_MaterialViewportWidget.h" AZ_POP_DISABLE_WARNING -#include #include -namespace Platform -{ - void ProcessInput(void* message); -} - - namespace MaterialEditor { @@ -40,11 +32,6 @@ namespace MaterialEditor { m_ui->setupUi(this); - if (auto dispatcher = QAbstractEventDispatcher::instance()) - { - dispatcher->installNativeEventFilter(this); - } - // The viewport context created by AtomToolsFramework::RenderViewportWidget has no name. // Systems like frame capturing and post FX expect there to be a context with DefaultViewportContextName auto viewportContextManager = AZ::Interface::Get(); @@ -54,13 +41,4 @@ namespace MaterialEditor m_renderer = AZStd::make_unique(GetViewportContext()->GetWindowContext()); GetControllerList()->Add(m_renderer->GetController()); } - - // This is a temporary fix to get input working in Qt window, otherwise it wont receive input events - // This will later be handled on the QApplication subclass level - bool MaterialViewportWidget::nativeEventFilter(const QByteArray& /*eventType*/, void* message, long* /*result*/) - { - Platform::ProcessInput(message); - - return false; - } } // namespace MaterialEditor diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h index 438c7b9a9e..2a71dcc1df 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h @@ -8,12 +8,10 @@ #pragma once #if !defined(Q_MOC_RUN) -#include #include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT #include -#include AZ_POP_DISABLE_WARNING #endif @@ -38,14 +36,11 @@ namespace MaterialEditor class MaterialViewportWidget : public AtomToolsFramework::RenderViewportWidget - , public QAbstractNativeEventFilter { public: MaterialViewportWidget(QWidget* parent = nullptr); QScopedPointer m_ui; AZStd::unique_ptr m_renderer; - - bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; }; } // namespace MaterialEditor diff --git a/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake index 2b554a852e..d4c4364ba7 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake @@ -10,5 +10,4 @@ set(FILES Source/main.cpp Source/MaterialEditorApplication.cpp Source/MaterialEditorApplication.h - tool_dependencies.cmake ) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/tool_dependencies.cmake b/Gems/Atom/Tools/MaterialEditor/Code/tool_dependencies.cmake deleted file mode 100644 index 8803be3852..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/tool_dependencies.cmake +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# - -set(GEM_DEPENDENCIES - Gem::Atom_RHI_Null.Private - Gem::Atom_RHI_DX12.Private - Gem::Atom_RHI_Vulkan.Private - Gem::Atom_RHI.Private - Gem::Atom_Component_DebugCamera - Gem::Atom_RPI.Editor - Gem::Atom_RPI.Builders - Gem::Atom_Feature_Common.Editor - Gem::AtomToolsFramework.Editor - Gem::AtomLyIntegration_CommonFeatures.Editor - Gem::EditorPythonBindings.Editor - Gem::ImageProcessingAtom.Editor -) diff --git a/Gems/Atom/Tools/MaterialEditor/MaterialEditor.xml b/Gems/Atom/Tools/MaterialEditor/MaterialEditor.xml deleted file mode 100644 index 9f91001f66..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/MaterialEditor.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Gems/Atom/Tools/MaterialEditor/gem.json b/Gems/Atom/Tools/MaterialEditor/gem.json index 83d9909a84..85ff434eab 100644 --- a/Gems/Atom/Tools/MaterialEditor/gem.json +++ b/Gems/Atom/Tools/MaterialEditor/gem.json @@ -8,7 +8,16 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "AtomToolsFramework", + "Atom_RPI", + "Atom_RHI", + "Atom_Feature_Common", + "ImageProcessingAtom", + "Atom_Component_DebugCamera", + "CommonFeaturesAtom", + "LyShine" + ] } diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt b/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt index d7e415a279..d384378b09 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/CMakeLists.txt @@ -43,6 +43,7 @@ ly_add_target( NAMESPACE Gem AUTOMOC AUTOUIC + AUTORCC FILES_CMAKE shadermanagementconsolewindow_files.cmake INCLUDE_DIRECTORIES @@ -64,6 +65,8 @@ ly_add_target( FILES_CMAKE shadermanagementconsole_files.cmake ${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + PLATFORM_INCLUDE_FILES + ${pal_source_dir}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake INCLUDE_DIRECTORIES PRIVATE . @@ -78,27 +81,16 @@ ly_add_target( Gem::ShaderManagementConsole.Window Gem::ShaderManagementConsole.Document RUNTIME_DEPENDENCIES - Gem::Atom_RHI_DX12.Private - Gem::Atom_RHI_Vulkan.Private - Gem::Atom_RHI.Private - Gem::Atom_RPI.Private - Gem::Atom_RPI.Builders - Gem::Atom_Feature_Common.Editor + Gem::AtomToolsFramework.Editor Gem::EditorPythonBindings.Editor ) +ly_set_gem_variant_to_load(TARGETS ShaderManagementConsole VARIANTS Tools) + # Add build dependency to Editor for the ShaderManagementConsole application since # Editor opens up the ShaderManagementConsole ly_add_dependencies(Editor Gem::ShaderManagementConsole) -ly_add_target_dependencies( - TARGETS - ShaderManagementConsole - DEPENDENCIES_FILES - tool_dependencies.cmake - Source/Platform/${PAL_PLATFORM_NAME}/tool_dependencies_${PAL_PLATFORM_NAME_LOWERCASE}.cmake -) - # Inject the project path into the ShaderManagementConsole VS debugger command arguments if the build system being invoked # in a project centric view if(NOT PROJECT_NAME STREQUAL "O3DE") @@ -108,9 +100,14 @@ endif() # Adds the ShaderManagementConsole target as a C preprocessor define so that it can be used as a Settings Registry # specialization in order to look up the generated .setreg which contains the dependencies # specified for the target. -set_source_files_properties( - Source/ShaderManagementConsoleApplication.cpp - PROPERTIES - COMPILE_DEFINITIONS - LY_CMAKE_TARGET="ShaderManagementConsole" -) +if(TARGET ShaderManagementConsole) + set_source_files_properties( + Source/ShaderManagementConsoleApplication.cpp + PROPERTIES + COMPILE_DEFINITIONS + LY_CMAKE_TARGET="ShaderManagementConsole" + ) +else() + message(FATAL_ERROR "Cannot set LY_CMAKE_TARGET define to ShaderManagementConsole as the target doesn't exist anymore." + " Perhaps it has been renamed") +endif() diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/PAL_linux.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/PAL_linux.cmake index 762ac16004..b5332e2e15 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/PAL_linux.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/PAL_linux.cmake @@ -6,4 +6,4 @@ # # -set(PAL_TRAIT_ATOM_SHADER_MANAGEMENT_CONSOLE_APPLICATION_SUPPORTED FALSE) +set(PAL_TRAIT_ATOM_SHADER_MANAGEMENT_CONSOLE_APPLICATION_SUPPORTED TRUE) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h new file mode 100644 index 0000000000..2897402f04 --- /dev/null +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Linux.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#define AZ_TRAIT_SHADER_MANAGEMENT_CONSOLE_EXT "" + diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Platform.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Platform.h new file mode 100644 index 0000000000..d381cc79d0 --- /dev/null +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/ShaderManagementConsole_Traits_Platform.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/platform_linux_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/platform_linux_files.cmake index c2c5a11c4c..11babcdf15 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/platform_linux_files.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/platform_linux_files.cmake @@ -7,4 +7,6 @@ # set(FILES + ShaderManagementConsole_Traits_Platform.h + ShaderManagementConsole_Traits_Linux.h ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/tool_dependencies_linux.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/tool_dependencies_linux.cmake index 5bf4d7cb7e..b2885100e9 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/tool_dependencies_linux.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Linux/tool_dependencies_linux.cmake @@ -6,5 +6,5 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Mac.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Mac.cpp deleted file mode 100644 index 6a05b895e0..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/ShaderManagementConsole_Mac.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include - -namespace Platform -{ - void ProcessInput(void* message) - { - AZ_Warning("Shader Management Console", false, "ProcessInput() function is not implemented"); - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - AZ_Warning("Shader Management Console", false, "GetWindowHandle() function is not implemented"); - AZ_UNUSED(winId); - return nullptr; - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - AZ_Warning("Shader Management Console", false, "GetClientAreaSize() function is not implemented"); - AZ_UNUSED(window); - return AzFramework::WindowSize{1,1}; - } -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/platform_mac_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/platform_mac_files.cmake index e48e836b73..6fe2859a89 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/platform_mac_files.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/platform_mac_files.cmake @@ -9,5 +9,4 @@ set(FILES ShaderManagementConsole_Traits_Platform.h ShaderManagementConsole_Traits_Mac.h - ShaderManagementConsole_Mac.cpp ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/tool_dependencies_mac.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/tool_dependencies_mac.cmake index 5bf4d7cb7e..b2885100e9 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/tool_dependencies_mac.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Mac/tool_dependencies_mac.cmake @@ -6,5 +6,5 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Windows.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Windows.cpp deleted file mode 100644 index ff94ef1196..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/ShaderManagementConsole_Windows.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include - -namespace Platform -{ - void ProcessInput(void* message) - { - MSG* msg = (MSG*)message; - - // Ensure that the Windows WM_INPUT messages get passed through to the AzFramework input system, - // but only while in game mode so we don't accumulate raw input events before we start actually - // ticking the input devices, otherwise the queued events will get sent when entering game mode. - if (msg->message == WM_INPUT) - { - UINT rawInputSize; - const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER); - GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, NULL, &rawInputSize, rawInputHeaderSize); - - LPBYTE rawInputBytes = new BYTE[rawInputSize]; - GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize); - - RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes; - - AzFramework::RawInputNotificationBusWindows::Broadcast( - &AzFramework::RawInputNotificationBusWindows::Events::OnRawInputEvent, *rawInput); - } - } - - AzFramework::NativeWindowHandle GetWindowHandle(WId winId) - { - return reinterpret_cast(winId); - } - - AzFramework::WindowSize GetClientAreaSize(AzFramework::NativeWindowHandle window) - { - RECT r; - if (GetWindowRect(reinterpret_cast(window), &r)) - { - return AzFramework::WindowSize{aznumeric_cast(r.right - r.left), aznumeric_cast(r.bottom - r.top)}; - } - else - { - AZ_Assert(false, "Failed to get dimensions for window"); - return AzFramework::WindowSize{}; - } - } -} diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/platform_windows_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/platform_windows_files.cmake index 4ec319f402..b978cedeb4 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/platform_windows_files.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/platform_windows_files.cmake @@ -9,6 +9,5 @@ set(FILES ShaderManagementConsole_Traits_Platform.h ShaderManagementConsole_Traits_Windows.h - ShaderManagementConsole_Windows.cpp ShaderManagementConsole.rc ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/tool_dependencies_windows.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/tool_dependencies_windows.cmake index 374438983f..e1e811ff67 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/tool_dependencies_windows.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/Windows/tool_dependencies_windows.cmake @@ -6,6 +6,6 @@ # # -set(GEM_DEPENDENCIES +set(LY_RUNTIME_DEPENDENCIES Gem::QtForPython.Editor ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/iOS/tool_dependencies_ios.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/iOS/tool_dependencies_ios.cmake deleted file mode 100644 index 5bf4d7cb7e..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Platform/iOS/tool_dependencies_ios.cmake +++ /dev/null @@ -1,10 +0,0 @@ -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# - -set(GEM_DEPENDENCIES -) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/tool_dependencies.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/tool_dependencies.cmake deleted file mode 100644 index 8803be3852..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/tool_dependencies.cmake +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# - -set(GEM_DEPENDENCIES - Gem::Atom_RHI_Null.Private - Gem::Atom_RHI_DX12.Private - Gem::Atom_RHI_Vulkan.Private - Gem::Atom_RHI.Private - Gem::Atom_Component_DebugCamera - Gem::Atom_RPI.Editor - Gem::Atom_RPI.Builders - Gem::Atom_Feature_Common.Editor - Gem::AtomToolsFramework.Editor - Gem::AtomLyIntegration_CommonFeatures.Editor - Gem::EditorPythonBindings.Editor - Gem::ImageProcessingAtom.Editor -) diff --git a/Gems/Atom/gem.json b/Gems/Atom/gem.json index 99ca26025a..1f5e4a37f3 100644 --- a/Gems/Atom/gem.json +++ b/Gems/Atom/gem.json @@ -5,8 +5,24 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Atom Renderer Gem provides Atom Renderer and its associated tools (such as Material Editor), utilites, libraries, and interfaces.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Core"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Core" + ], "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom/", + "dependencies": [ + "Atom_Feature_Common", + "AtomShader", + "Atom_Bootstrap", + "Atom_Component_DebugCamera", + "Atom_RHI", + "Atom_RPI", + "AtomToolsFramework", + "MaterialEditor", + "Atom_AtomBridge" + ] } diff --git a/Gems/AtomContent/ReferenceMaterials/gem.json b/Gems/AtomContent/ReferenceMaterials/gem.json index 9e145c20cc..f697d50bc4 100644 --- a/Gems/AtomContent/ReferenceMaterials/gem.json +++ b/Gems/AtomContent/ReferenceMaterials/gem.json @@ -7,5 +7,6 @@ "summary": "Atom Asset Gem with a library of reference materials for StandardPBR (and others in the future)", "canonical_tags": ["Gem"], "user_tags": ["Assets", "PBR", "Materials"], - "icon_path": "preview.png" + "icon_path": "preview.png", + "dependencies": [] } diff --git a/Gems/AtomContent/Sponza/gem.json b/Gems/AtomContent/Sponza/gem.json index 3fc76927e1..64de0e5da0 100644 --- a/Gems/AtomContent/Sponza/gem.json +++ b/Gems/AtomContent/Sponza/gem.json @@ -5,7 +5,12 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "A standard test scene for Global Illumination (forked from crytek sponza scene)", - "canonical_tags": ["Gem"], - "user_tags": ["Assets"], - "requirements": "" + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets" + ], + "requirements": "", + "dependencies": [] } diff --git a/Gems/AtomContent/gem.json b/Gems/AtomContent/gem.json index 6fcb8903e4..1bffe7d989 100644 --- a/Gems/AtomContent/gem.json +++ b/Gems/AtomContent/gem.json @@ -5,8 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Atom Content Gem provides assets for Atom Renderer and a modified version of the Pixar Look Development Studio.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Assets", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Assets", + "Tools" + ], "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom-content/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom-content/", + "dependencies": [ + "ReferenceMaterials", + "Sponza" + ] } diff --git a/Gems/AtomLyIntegration/AtomBridge/gem.json b/Gems/AtomLyIntegration/AtomBridge/gem.json index 49a56d2201..1d48d8be61 100644 --- a/Gems/AtomLyIntegration/AtomBridge/gem.json +++ b/Gems/AtomLyIntegration/AtomBridge/gem.json @@ -8,7 +8,26 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RPI", + "Atom_Bootstrap", + "Atom_RHI", + "Atom_RHI_Null", + "Atom_Feature_Common", + "Atom_Component_DebugCamera", + "AtomImGuiTools", + "CommonFeaturesAtom", + "EMotionFX_Atom", + "ImguiAtom", + "AtomFont", + "AtomViewportDisplayInfo", + "Atom", + "AtomShader", + "ImageProcessingAtom", + "AtomToolsFramework", + "AtomViewportDisplayIcons", + "MaterialEditor" + ] } diff --git a/Gems/AtomLyIntegration/AtomFont/gem.json b/Gems/AtomLyIntegration/AtomFont/gem.json index 7e1a71db86..ed5b488de7 100644 --- a/Gems/AtomLyIntegration/AtomFont/gem.json +++ b/Gems/AtomLyIntegration/AtomFont/gem.json @@ -8,7 +8,11 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI", + "Atom_RPI", + "Atom_AtomBridge" + ] } diff --git a/Gems/AtomLyIntegration/AtomImGuiTools/gem.json b/Gems/AtomLyIntegration/AtomImGuiTools/gem.json index ff20d5a19f..564eeedee2 100644 --- a/Gems/AtomLyIntegration/AtomImGuiTools/gem.json +++ b/Gems/AtomLyIntegration/AtomImGuiTools/gem.json @@ -12,5 +12,9 @@ "Debug", "Rendering" ], - "requirements": "" + "requirements": "", + "dependencies": [ + "ImguiAtom", + "Atom" + ] } diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp index 6ea6a8e85f..c0ca2fe993 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.cpp @@ -82,6 +82,8 @@ namespace AZ::Render void AtomViewportDisplayIconsSystemComponent::Activate() { + m_drawContextRegistered = false; + AzToolsFramework::EditorViewportIconDisplay::Register(this); Bootstrap::NotificationBus::Handler::BusConnect(); @@ -97,9 +99,10 @@ namespace AZ::Render { return; } - if (perViewportDynamicDrawInterface) + if (perViewportDynamicDrawInterface && m_drawContextRegistered) { perViewportDynamicDrawInterface->UnregisterDynamicDrawContext(m_drawContextName); + m_drawContextRegistered = false; } AzToolsFramework::EditorViewportIconDisplay::Unregister(this); @@ -367,6 +370,8 @@ namespace AZ::Render drawContext->EndInit(); }); + m_drawContextRegistered = true; + Data::AssetBus::Handler::BusDisconnect(); } } // namespace AZ::Render diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h index 25cc7eb8f6..a0914e7e2d 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Code/Source/AtomViewportDisplayIconsSystemComponent.h @@ -77,6 +77,8 @@ namespace AZ }; AZStd::unordered_map m_iconData; IconId m_currentId = 0; + + bool m_drawContextRegistered = false; }; } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json index 52dbb219d3..a2a7ba4b77 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json @@ -8,7 +8,12 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI", + "Atom_RPI", + "Atom_Bootstrap", + "Atom_AtomBridge" + ] } diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json index f277d6cf82..be5cc96f95 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json +++ b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json @@ -8,7 +8,10 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_RHI", + "Atom_RPI" + ] } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h index 3b65750b5f..ace16ba6ca 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h @@ -33,6 +33,16 @@ namespace AZ virtual const MaterialAssignmentMap& GetMaterialOverrides() const = 0; //! Clear all material overrides virtual void ClearAllMaterialOverrides() = 0; + //! Clear non-lod material overrides + virtual void ClearModelMaterialOverrides() = 0; + //! Clear lod material overrides + virtual void ClearLodMaterialOverrides() = 0; + //! Clear residual materials that don't correspond to the associated model + virtual void ClearIncompatibleMaterialOverrides() = 0; + //! Clear materials that reference missing assets + virtual void ClearInvalidMaterialOverrides() = 0; + //! Repair materials that reference missing assets by assigning the default asset + virtual void RepairInvalidMaterialOverrides() = 0; //! Set default material override virtual void SetDefaultMaterialOverride(const AZ::Data::AssetId& materialAssetId) = 0; //! Get default material override diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingBus.h new file mode 100644 index 0000000000..32b1c97429 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingBus.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +namespace AZ +{ + namespace Render + { + class HDRColorGradingRequests + : public ComponentBus + { + public: + AZ_RTTI(AZ::Render::HDRColorGradingRequests, "{E414A96B-0AA7-4574-ABC0-968B1F5CEE56}"); + + /// Overrides the default AZ::EBusTraits handler policy to allow one listener only. + static const EBusHandlerPolicy HandlerPolicy = EBusHandlerPolicy::Single; + virtual ~HDRColorGradingRequests() {} + // Auto-gen virtual getters/setters... +#include +#include +#include + }; + + typedef AZ::EBus HDRColorGradingRequestBus; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingComponentConfig.h new file mode 100644 index 0000000000..fe7bc874a4 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingComponentConfig.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + class HDRColorGradingComponentConfig final + : public ComponentConfig + { + public: + AZ_RTTI(AZ::Render::HDRColorGradingComponentConfig, "{8613EA4A-6E1C-49AD-87F3-9FECCB7EA36D}", AZ::ComponentConfig); + + static void Reflect(ReflectContext* context); + + // Generate members... +#include +#include +#include + + // Generate Getters/Setters... +#include +#include +#include + + void CopySettingsTo(HDRColorGradingSettingsInterface* settings); + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.cpp index c10e7e2e30..c5d61dd54a 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.cpp @@ -32,7 +32,7 @@ namespace AZ if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { editContext->Class( - "Decal (Atom)", "The Decal component allows an entity to project a texture or material onto a mesh") + "Decal", "The Decal component allows an entity to project a texture or material onto a mesh") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "Atom") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Decal.svg") @@ -97,13 +97,25 @@ namespace AZ BaseClass::Deactivate(); } - AZ::Transform EditorDecalComponent::GetTransform() const + AZ::Transform EditorDecalComponent::GetWorldTransform() const { AZ::Transform transform = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(transform, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); return transform; } + AZ::Matrix3x4 EditorDecalComponent::GetWorldTransformWithNonUniformScale() const + { + const AZ::Transform worldTransform = GetWorldTransform(); + const AZ::Matrix3x3 rotationMat = AZ::Matrix3x3::CreateFromQuaternion(worldTransform.GetRotation()); + + const AZ::Vector3 nonUniformScale = m_controller.m_cachedNonUniformScale * worldTransform.GetUniformScale(); + const AZ::Matrix3x3 nonUniformScaleMat = AZ::Matrix3x3::CreateScale(nonUniformScale); + const AZ::Matrix3x3 rotationAndScale = rotationMat * nonUniformScaleMat; + + return AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(rotationAndScale, worldTransform.GetTranslation()); + } + void EditorDecalComponent::DisplayEntityViewport( [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) @@ -113,11 +125,9 @@ namespace AZ return; } - AZ::Transform transform = GetTransform(); - - debugDisplay.SetColor(AZ::Colors::Red); - debugDisplay.PushMatrix(transform); + const AZ::Matrix3x4 transform = GetWorldTransformWithNonUniformScale(); + debugDisplay.PushPremultipliedMatrix(transform); debugDisplay.DrawWireBox(-AZ::Vector3::CreateOne(), AZ::Vector3::CreateOne()); AZ::Vector3 x1 = AZ::Vector3(-1, 0, 1); @@ -136,7 +146,7 @@ namespace AZ // Two diagonal edges debugDisplay.DrawLine(p0, p2); debugDisplay.DrawLine(p1, p3); - debugDisplay.PopMatrix(); + debugDisplay.PopPremultipliedMatrix(); } AZ::Aabb EditorDecalComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.h index f0748dfef5..1ca88d23fc 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.h @@ -56,7 +56,11 @@ namespace AZ private: - AZ::Transform GetTransform() const; + // Returns the component transform which includes uniform-scale, rotation and translation + AZ::Transform GetWorldTransform() const; + + // Returns the full transform, including both the uniform scale and non-uniform scale along with rotation and translation + AZ::Matrix3x4 GetWorldTransformWithNonUniformScale() const; //! EditorRenderComponentAdapter overrides ... u32 OnConfigurationChanged() override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp index c243522257..b7bf191bc9 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp @@ -32,9 +32,6 @@ namespace AZ const char* EditorMaterialComponent::GenerateMaterialsButtonText = "Generate/Manage Source Materials..."; const char* EditorMaterialComponent::GenerateMaterialsToolTipText = "Generate editable source material files from materials provided by the model."; - const char* EditorMaterialComponent::ResetMaterialsButtonText = "Reset Materials"; - const char* EditorMaterialComponent::ResetMaterialsToolTipText = "Clear all settings, materials, and properties then rebuild material slots from the associated model."; - // Update serialized data to the new format and data types bool EditorMaterialComponent::ConvertVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) { @@ -178,43 +175,74 @@ namespace AZ menu->addSeparator(); - action = menu->addAction(ResetMaterialsButtonText, [this]() { ResetMaterialSlots(); }); - action->setToolTip(ResetMaterialsToolTipText); + action = menu->addAction("Clear All Materials", [this]() { + AzToolsFramework::ScopedUndoBatch undoBatch("Clearing all materials."); + SetDirty(); - menu->addSeparator(); + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::ClearAllMaterialOverrides); + + m_materialSlotsByLodEnabled = false; + + UpdateMaterialSlots(); + }); + action->setToolTip("Clear all materials and properties then rebuild material slots from the associated model."); action = menu->addAction("Clear Model Materials", [this]() { AzToolsFramework::ScopedUndoBatch undoBatch("Clearing model materials."); SetDirty(); - for (auto& materialSlotPair : GetMaterialSlots()) - { - EditorMaterialComponentSlot* materialSlot = materialSlotPair.second; - if (materialSlot->m_id.IsSlotIdOnly()) - { - materialSlot->Clear(); - } - } - }); + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::ClearModelMaterialOverrides); + + UpdateMaterialSlots(); + }); + action->setToolTip("Clear model materials and properties then rebuild material slots from the associated model."); + action = menu->addAction("Clear LOD Materials", [this]() { AzToolsFramework::ScopedUndoBatch undoBatch("Clearing LOD materials."); SetDirty(); - for (auto& materialSlotPair : GetMaterialSlots()) - { - EditorMaterialComponentSlot* materialSlot = materialSlotPair.second; - if (materialSlot->m_id.IsLodAndSlotId()) - { - materialSlot->Clear(); - } - } - }); - action->setEnabled(m_materialSlotsByLodEnabled); + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::ClearLodMaterialOverrides); + + m_materialSlotsByLodEnabled = false; + + UpdateMaterialSlots(); + }); + action->setToolTip("Clear LOD materials and properties then rebuild material slots from the associated model."); + + action = menu->addAction("Clear Incompatible Materials", [this]() { + AzToolsFramework::ScopedUndoBatch undoBatch("Clearing incompatible materials."); + SetDirty(); + + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::ClearIncompatibleMaterialOverrides); + + UpdateMaterialSlots(); + }); + action->setToolTip("Clear residual materials that don't correspond to the associated model."); + + action = menu->addAction("Clear Invalid Materials", [this]() { + AzToolsFramework::ScopedUndoBatch undoBatch("Clearing invalid materials."); + SetDirty(); + + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::ClearInvalidMaterialOverrides); + + UpdateMaterialSlots(); + }); + action->setToolTip("Clear materials that reference missing assets."); + + action = menu->addAction("Repair Invalid Materials", [this]() { + AzToolsFramework::ScopedUndoBatch undoBatch("Repairing invalid materials."); + SetDirty(); + + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::RepairInvalidMaterialOverrides); + + UpdateMaterialSlots(); + }); + action->setToolTip("Repair materials that reference missing assets by assigning the default asset."); } void EditorMaterialComponent::SetPrimaryAsset(const AZ::Data::AssetId& assetId) { - m_controller.SetDefaultMaterialOverride(assetId); + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::SetDefaultMaterialOverride, assetId); MaterialComponentNotificationBus::Event(GetEntityId(), &MaterialComponentNotifications::OnMaterialsEdited); @@ -249,14 +277,18 @@ namespace AZ m_materialSlots = {}; m_materialSlotsByLod = {}; - const MaterialComponentConfig& config = m_controller.GetConfiguration(); + // Get current material assignments + MaterialAssignmentMap currentMaterials; + MaterialComponentRequestBus::EventResult( + currentMaterials, GetEntityId(), &MaterialComponentRequestBus::Events::GetMaterialOverrides); // Get the known material assignment slots from the associated model or other source - MaterialAssignmentMap materialsFromSource; - MaterialReceiverRequestBus::EventResult(materialsFromSource, GetEntityId(), &MaterialReceiverRequestBus::Events::GetMaterialAssignments); + MaterialAssignmentMap originalMaterials; + MaterialComponentRequestBus::EventResult( + originalMaterials, GetEntityId(), &MaterialComponentRequestBus::Events::GetOriginalMaterialAssignments); // Generate the table of editable materials using the source data to define number of groups, elements, and initial values - for (const auto& materialPair : materialsFromSource) + for (const auto& materialPair : originalMaterials) { // Setup the material slot entry EditorMaterialComponentSlot slot; @@ -264,7 +296,7 @@ namespace AZ slot.m_id = materialPair.first; // if material is present in controller configuration, assign its data - const MaterialAssignment& materialFromController = GetMaterialAssignmentFromMap(config.m_materials, slot.m_id); + const MaterialAssignment& materialFromController = GetMaterialAssignmentFromMap(currentMaterials, slot.m_id); slot.m_materialAsset = materialFromController.m_materialAsset; if (slot.m_id.IsDefault()) @@ -289,7 +321,7 @@ namespace AZ } } - // Sort all of the slots by label to ensure stable index values (materialsFromSource is an unordered map) + // Sort all of the slots by label to ensure stable index values (originalMaterials is an unordered map) AZStd::sort(m_materialSlots.begin(), m_materialSlots.end(), [](const auto& a, const auto& b) { return a.GetLabel() < b.GetLabel(); }); @@ -305,49 +337,36 @@ namespace AZ &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree); } - AZ::u32 EditorMaterialComponent::ResetMaterialSlots() - { - AzToolsFramework::ScopedUndoBatch undoBatch("Resetting materials."); - SetDirty(); - - m_controller.SetMaterialOverrides(MaterialAssignmentMap()); - UpdateMaterialSlots(); - - m_materialSlotsByLodEnabled = false; - - MaterialComponentNotificationBus::Event(GetEntityId(), &MaterialComponentNotifications::OnMaterialsEdited); - - AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( - &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree); - - return AZ::Edit::PropertyRefreshLevels::EntireTree; - } - AZ::u32 EditorMaterialComponent::OpenMaterialExporter() { AzToolsFramework::ScopedUndoBatch undoBatch("Generating materials."); SetDirty(); - // First generating a unique set of all material asset IDs that will be used for source data generation - AZStd::unordered_map assetIdMap; + MaterialAssignmentMap originalMaterials; + MaterialComponentRequestBus::EventResult( + originalMaterials, GetEntityId(), &MaterialComponentRequestBus::Events::GetOriginalMaterialAssignments); - auto materialSlots = GetMaterialSlots(); - for (auto& materialSlotPair : materialSlots) + // Generate a unique set of all material asset IDs that will be used for source data generation + AZStd::unordered_map assetIdToSlotNameMap; + for (const auto& materialPair : originalMaterials) { - Data::AssetId defaultMaterialAssetId = materialSlotPair.second->GetDefaultAssetId(); - if (defaultMaterialAssetId.IsValid()) + const Data::AssetId originalAssetId = materialPair.second.m_materialAsset.GetId(); + if (originalAssetId.IsValid()) { - assetIdMap[defaultMaterialAssetId] = materialSlotPair.second->GetLabel(); + MaterialComponentRequestBus::EventResult( + assetIdToSlotNameMap[originalAssetId], GetEntityId(), &MaterialComponentRequestBus::Events::GetMaterialSlotLabel, + materialPair.first); } } // Convert the unique set of asset IDs into export items that can be configured in the dialog // The order should not matter because the table in the dialog can sort itself for a specific row EditorMaterialComponentExporter::ExportItemsContainer exportItems; - for (auto assetIdInfo : assetIdMap) + exportItems.reserve(assetIdToSlotNameMap.size()); + + for (const auto& [assetId, slotName] : assetIdToSlotNameMap) { - EditorMaterialComponentExporter::ExportItem exportItem{ assetIdInfo.first, assetIdInfo.second }; - exportItems.push_back(exportItem); + exportItems.emplace_back(assetId, slotName); } // Display the export dialog so that the user can configure how they want different materials to be exported @@ -363,16 +382,17 @@ namespace AZ const auto& assetIdOutcome = AZ::RPI::AssetUtils::MakeAssetId(exportItem.GetExportPath(), 0); if (assetIdOutcome) { - for (auto& materialSlotPair : materialSlots) + for (const auto& materialPair : originalMaterials) { - EditorMaterialComponentSlot* editorMaterialSlot = materialSlotPair.second; - - if (editorMaterialSlot) + // We need to check whether replaced material corresponds to this slot's default material. + const Data::AssetId originalAssetId = materialPair.second.m_materialAsset.GetId(); + if (originalAssetId == exportItem.GetOriginalAssetId()) { - // We need to check whether replaced material corresponds to this slot's default material. - if (editorMaterialSlot->GetDefaultAssetId() == exportItem.GetOriginalAssetId()) + if (m_materialSlotsByLodEnabled || !materialPair.first.IsLodAndSlotId()) { - editorMaterialSlot->SetAsset(assetIdOutcome.GetValue()); + MaterialComponentRequestBus::Event( + GetEntityId(), &MaterialComponentRequestBus::Events::SetMaterialOverride, materialPair.first, + assetIdOutcome.GetValue()); } } } @@ -380,12 +400,9 @@ namespace AZ } } - MaterialComponentNotificationBus::Event(GetEntityId(), &MaterialComponentNotifications::OnMaterialsEdited); + UpdateMaterialSlots(); - AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast( - &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_AttributesAndValues); - - return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; + return AZ::Edit::PropertyRefreshLevels::EntireTree; } AZ::u32 EditorMaterialComponent::OnLodsToggled() @@ -395,15 +412,10 @@ namespace AZ if (!m_materialSlotsByLodEnabled) { - MaterialComponentConfig config = m_controller.GetConfiguration(); - AZStd::erase_if(config.m_materials, [](const auto& item) { - const auto& [key, value] = item; - return key.m_lodIndex != MaterialAssignmentId::NonLodIndex; - }); - m_controller.SetMaterialOverrides(config.m_materials); + MaterialComponentRequestBus::Event(GetEntityId(), &MaterialComponentRequestBus::Events::ClearLodMaterialOverrides); } - MaterialComponentNotificationBus::Event(GetEntityId(), &MaterialComponentNotifications::OnMaterialsEdited); + UpdateMaterialSlots(); return AZ::Edit::PropertyRefreshLevels::EntireTree; } @@ -440,41 +452,5 @@ namespace AZ { return AZStd::string::format("LOD %d", lodIndex); } - - template - void EditorMaterialComponent::BuildMaterialSlotMap(ComponentType& component, ContainerType& materialSlots) - { - materialSlots[DefaultMaterialAssignmentId] = &component.m_defaultMaterialSlot; - - for (auto& slot : component.m_materialSlots) - { - materialSlots[slot.m_id] = &slot; - } - - if (component.m_materialSlotsByLodEnabled) - { - for (auto& slotsForLod : component.m_materialSlotsByLod) - { - for (auto& slot : slotsForLod) - { - materialSlots[slot.m_id] = &slot; - } - } - } - } - - AZStd::unordered_map EditorMaterialComponent::GetMaterialSlots() - { - AZStd::unordered_map materialSlots; - BuildMaterialSlotMap(*this, materialSlots); - return AZStd::move(materialSlots); - } - - AZStd::unordered_map EditorMaterialComponent::GetMaterialSlots() const - { - AZStd::unordered_map materialSlots; - BuildMaterialSlotMap(*this, materialSlots); - return AZStd::move(materialSlots); - } } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h index 6a218e23b7..f8895d994f 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.h @@ -58,9 +58,6 @@ namespace AZ // controller configuration then those values will be assigned to the editor component slots. void UpdateMaterialSlots(); - // Clears all values related to the material component and regenerates the editor slots - AZ::u32 ResetMaterialSlots(); - // Opens the source material export dialog and updates editor material slots based on // selected actions AZ::u32 OpenMaterialExporter(); @@ -82,10 +79,6 @@ namespace AZ // Evaluate if materials can be edited bool IsEditingAllowed() const; - template - static void BuildMaterialSlotMap(ComponentType& component, ContainerType& materialSlots); - AZStd::unordered_map GetMaterialSlots(); - AZStd::unordered_map GetMaterialSlots() const; AZStd::string GetLabelForLod(int lodIndex) const; AZStd::string m_message; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentExporter.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentExporter.cpp index f55da28fa6..14cc28b91e 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentExporter.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentExporter.cpp @@ -55,6 +55,10 @@ namespace AZ bool OpenExportDialog(ExportItemsContainer& exportItems) { + // Sort material entries so they are ordered by name in the table + AZStd::sort(exportItems.begin(), exportItems.end(), + [](const auto& a, const auto& b) { return a.GetMaterialSlotName() < b.GetMaterialSlotName(); }); + QWidget* activeWindow = nullptr; AzToolsFramework::EditorWindowRequestBus::BroadcastResult(activeWindow, &AzToolsFramework::EditorWindowRequests::GetAppMainWindow); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp index 37a9ee7b93..363221d3fc 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentSlot.cpp @@ -140,7 +140,7 @@ namespace AZ void EditorMaterialComponentSlot::SetAsset(const Data::AssetId& assetId) { - m_materialAsset.Create(assetId); + m_materialAsset = AZ::Data::Asset(assetId, AZ::AzTypeInfo::Uuid()); MaterialComponentRequestBus::Event( m_entityId, &MaterialComponentRequestBus::Events::SetMaterialOverride, m_id, m_materialAsset.GetId()); OnDataChanged(); @@ -164,7 +164,7 @@ namespace AZ void EditorMaterialComponentSlot::ClearToDefaultAsset() { - m_materialAsset.Create(GetDefaultAssetId()); + m_materialAsset = AZ::Data::Asset(GetDefaultAssetId(), AZ::AzTypeInfo::Uuid()); MaterialComponentRequestBus::Event( m_entityId, &MaterialComponentRequestBus::Events::SetMaterialOverride, m_id, m_materialAsset.GetId()); ClearOverrides(); @@ -204,7 +204,8 @@ namespace AZ const auto& assetIdOutcome = AZ::RPI::AssetUtils::MakeAssetId(exportItem.GetExportPath(), 0); if (assetIdOutcome) { - m_materialAsset.Create(assetIdOutcome.GetValue()); + m_materialAsset = AZ::Data::Asset( + assetIdOutcome.GetValue(), AZ::AzTypeInfo::Uuid()); changed = true; } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp index 14f81c5021..1d9f1e81dd 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp @@ -43,6 +43,11 @@ namespace AZ ->Event("SetDefaultMaterialOverride", &MaterialComponentRequestBus::Events::SetDefaultMaterialOverride) ->Event("GetDefaultMaterialOverride", &MaterialComponentRequestBus::Events::GetDefaultMaterialOverride) ->Event("ClearDefaultMaterialOverride", &MaterialComponentRequestBus::Events::ClearDefaultMaterialOverride) + ->Event("ClearModelMaterialOverrides", &MaterialComponentRequestBus::Events::ClearModelMaterialOverrides) + ->Event("ClearLodMaterialOverrides", &MaterialComponentRequestBus::Events::ClearLodMaterialOverrides) + ->Event("ClearIncompatibleMaterialOverrides", &MaterialComponentRequestBus::Events::ClearIncompatibleMaterialOverrides) + ->Event("ClearInvalidMaterialOverrides", &MaterialComponentRequestBus::Events::ClearInvalidMaterialOverrides) + ->Event("RepairInvalidMaterialOverrides", &MaterialComponentRequestBus::Events::RepairInvalidMaterialOverrides) ->Event("SetMaterialOverride", &MaterialComponentRequestBus::Events::SetMaterialOverride) ->Event("GetMaterialOverride", &MaterialComponentRequestBus::Events::GetMaterialOverride) ->Event("ClearMaterialOverride", &MaterialComponentRequestBus::Events::ClearMaterialOverride) @@ -275,10 +280,10 @@ namespace AZ MaterialAssignmentMap MaterialComponentController::GetOriginalMaterialAssignments() const { - MaterialAssignmentMap materialAssignmentMap; + MaterialAssignmentMap originalMaterials; MaterialReceiverRequestBus::EventResult( - materialAssignmentMap, m_entityId, &MaterialReceiverRequestBus::Events::GetMaterialAssignments); - return materialAssignmentMap; + originalMaterials, m_entityId, &MaterialReceiverRequestBus::Events::GetMaterialAssignments); + return originalMaterials; } MaterialAssignmentId MaterialComponentController::FindMaterialAssignmentId( @@ -348,6 +353,67 @@ namespace AZ } } + void MaterialComponentController::ClearModelMaterialOverrides() + { + AZStd::erase_if(m_configuration.m_materials, [](const auto& materialPair) { + return materialPair.first.IsSlotIdOnly(); + }); + QueueMaterialUpdateNotification(); + } + + void MaterialComponentController::ClearLodMaterialOverrides() + { + AZStd::erase_if(m_configuration.m_materials, [](const auto& materialPair) { + return materialPair.first.IsLodAndSlotId(); + }); + QueueMaterialUpdateNotification(); + } + + void MaterialComponentController::ClearIncompatibleMaterialOverrides() + { + const MaterialAssignmentMap& originalMaterials = GetOriginalMaterialAssignments(); + AZStd::erase_if(m_configuration.m_materials, [&originalMaterials](const auto& materialPair) { + return originalMaterials.find(materialPair.first) == originalMaterials.end(); + }); + QueueMaterialUpdateNotification(); + } + + void MaterialComponentController::ClearInvalidMaterialOverrides() + { + AZStd::erase_if(m_configuration.m_materials, [](const auto& materialPair) { + if (materialPair.second.m_materialAsset.GetId().IsValid()) + { + AZ::Data::AssetInfo assetInfo; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, + materialPair.second.m_materialAsset.GetId()); + return !assetInfo.m_assetId.IsValid(); + } + return false; + }); + QueueMaterialUpdateNotification(); + } + + void MaterialComponentController::RepairInvalidMaterialOverrides() + { + for (auto& materialPair : m_configuration.m_materials) + { + if (materialPair.second.m_materialAsset.GetId().IsValid()) + { + AZ::Data::AssetInfo assetInfo; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, + materialPair.second.m_materialAsset.GetId()); + if (!assetInfo.m_assetId.IsValid()) + { + materialPair.second.m_materialAsset = AZ::Data::Asset( + GetDefaultMaterialAssetId(materialPair.first), AZ::AzTypeInfo::Uuid()); + } + } + } + LoadMaterials(); + } + void MaterialComponentController::SetDefaultMaterialOverride(const AZ::Data::AssetId& materialAssetId) { SetMaterialOverride(DefaultMaterialAssignmentId, materialAssetId); @@ -363,9 +429,11 @@ namespace AZ ClearMaterialOverride(DefaultMaterialAssignmentId); } - void MaterialComponentController::SetMaterialOverride(const MaterialAssignmentId& materialAssignmentId, const AZ::Data::AssetId& materialAssetId) + void MaterialComponentController::SetMaterialOverride( + const MaterialAssignmentId& materialAssignmentId, const AZ::Data::AssetId& materialAssetId) { - m_configuration.m_materials[materialAssignmentId].m_materialAsset.Create(materialAssetId); + m_configuration.m_materials[materialAssignmentId].m_materialAsset = + AZ::Data::Asset(materialAssetId, AZ::AzTypeInfo::Uuid()); LoadMaterials(); } @@ -616,7 +684,6 @@ namespace AZ void MaterialComponentController::ClearAllPropertyOverrides() { - bool cleared = false; for (auto& materialPair : m_configuration.m_materials) { if (!materialPair.second.m_propertyOverrides.empty()) @@ -625,13 +692,8 @@ namespace AZ materialPair.second.RebuildInstance(); MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialInstanceCreated, materialPair.second); QueueMaterialUpdateNotification(); - cleared = true; } } - - if (cleared) - { - } } void MaterialComponentController::SetPropertyOverrides( diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h index c9dd330412..2bf15ca3b8 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h @@ -52,6 +52,11 @@ namespace AZ void SetMaterialOverrides(const MaterialAssignmentMap& materials) override; const MaterialAssignmentMap& GetMaterialOverrides() const override; void ClearAllMaterialOverrides() override; + void ClearModelMaterialOverrides() override; + void ClearLodMaterialOverrides() override; + void ClearIncompatibleMaterialOverrides() override; + void ClearInvalidMaterialOverrides() override; + void RepairInvalidMaterialOverrides() override; void SetDefaultMaterialOverride(const AZ::Data::AssetId& materialAssetId) override; const AZ::Data::AssetId GetDefaultMaterialOverride() const override; void ClearDefaultMaterialOverride() override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp index a83e2e9fa1..230130826e 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Module.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -93,6 +95,7 @@ namespace AZ DecalComponent::CreateDescriptor(), DirectionalLightComponent::CreateDescriptor(), BloomComponent::CreateDescriptor(), + HDRColorGradingComponent::CreateDescriptor(), DisplayMapperComponent::CreateDescriptor(), DepthOfFieldComponent::CreateDescriptor(), ExposureControlComponent::CreateDescriptor(), @@ -124,6 +127,7 @@ namespace AZ EditorDecalComponent::CreateDescriptor(), EditorDirectionalLightComponent::CreateDescriptor(), EditorBloomComponent::CreateDescriptor(), + EditorHDRColorGradingComponent::CreateDescriptor(), EditorDepthOfFieldComponent::CreateDescriptor(), EditorDisplayMapperComponent::CreateDescriptor(), EditorExposureControlComponent::CreateDescriptor(), diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp new file mode 100644 index 0000000000..49c8275141 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace AZ +{ + namespace Render + { + void EditorHDRColorGradingComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class()->Version(1); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "HDR Color Grading", "Tune and apply color grading in HDR.") + ->ClassElement(Edit::ClassElements::EditorData, "") + ->Attribute(Edit::Attributes::Category, "Atom") + ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing. + ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") // [GFX TODO ATOM-2672][PostFX] need to create icons for PostProcessing. + ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) + ->Attribute(Edit::Attributes::AutoExpand, true) + ->Attribute(Edit::Attributes::HelpPageURL, "https://") // [TODO ATOM-2672][PostFX] need to create page for PostProcessing. + ; + + editContext->Class( + "HDRColorGradingComponentControl", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &HDRColorGradingComponentController::m_configuration, "Configuration", "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ; + + editContext->Class("HDRColorGradingComponentConfig", "") + ->DataElement(Edit::UIHandlers::CheckBox, &HDRColorGradingComponentConfig::m_enabled, + "Enable HDR color grading", + "Enable HDR color grading.") + ->ClassElement(AZ::Edit::ClassElements::Group, "Color Adjustment") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_colorGradingExposure, "Exposure", "Exposure Value") + ->Attribute(Edit::Attributes::Min, AZStd::numeric_limits::lowest()) + ->Attribute(Edit::Attributes::Max, AZStd::numeric_limits::max()) + ->Attribute(Edit::Attributes::SoftMin, -20.0f) + ->Attribute(Edit::Attributes::SoftMax, 20.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_colorGradingContrast, "Contrast", "Contrast Value") + ->Attribute(Edit::Attributes::Min, -100.0f) + ->Attribute(Edit::Attributes::Max, 100.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_colorGradingPreSaturation, "Pre Saturation", "Pre Saturation Value") + ->Attribute(Edit::Attributes::Min, -100.0f) + ->Attribute(Edit::Attributes::Max, 100.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_colorGradingFilterIntensity, "Filter Intensity", "Filter Intensity Value") + ->Attribute(Edit::Attributes::Min, AZStd::numeric_limits::lowest()) + ->Attribute(Edit::Attributes::Max, AZStd::numeric_limits::max()) + ->Attribute(Edit::Attributes::SoftMin, -1.0f) + ->Attribute(Edit::Attributes::SoftMax, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_colorGradingFilterMultiply, "Filter Multiply", "Filter Multiply Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_colorFilterSwatch, "Color Filter Swatch", "Color Filter Swatch Value") + + ->ClassElement(AZ::Edit::ClassElements::Group, "White Balance") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_whiteBalanceKelvin, "Temperature", "Temperature in Kelvin") + ->Attribute(Edit::Attributes::Min, 1000.0f) + ->Attribute(Edit::Attributes::Max, 40000.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_whiteBalanceTint, "Tint", "Tint Value") + ->Attribute(Edit::Attributes::Min, -100.0f) + ->Attribute(Edit::Attributes::Max, 100.0f) + + ->ClassElement(AZ::Edit::ClassElements::Group, "Split Toning") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_splitToneWeight, "Split Tone Weight", "Modulates the split toning effect.") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_splitToneBalance, "Split Tone Balance", "Split Tone Balance Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_splitToneShadowsColor, "Split Tone Shadows Color", "Split Tone Shadows Color") + ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_splitToneHighlightsColor, "Split Tone Highlights Color", "Split Tone Highlights Color") + + ->ClassElement(AZ::Edit::ClassElements::Group, "Channel Mixing") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_channelMixingRed, "Channel Mixing Red", "Channel Mixing Red Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_channelMixingGreen, "Channel Mixing Green", "Channel Mixing Green Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_channelMixingBlue, "Channel Mixing Blue", "Channel Mixing Blue Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + + ->ClassElement(AZ::Edit::ClassElements::Group, "Shadow Midtones Highlights") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_smhWeight, "SMH Weight", "Modulates the SMH effect.") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_smhShadowsStart, "SMH Shadows Start", "SMH Shadows Start Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_smhShadowsEnd, "SMH Shadows End", "SMH Shadows End Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_smhHighlightsStart, "SMH Highlights Start", "SMH Highlights Start Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_smhHighlightsEnd, "SMH Highlights End", "SMH Highlights End Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_smhShadowsColor, "SMH Shadows Color", "SMH Shadows Color") + ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_smhMidtonesColor, "SMH Midtones Color", "SMH Midtones Color") + ->DataElement(AZ::Edit::UIHandlers::Color, &HDRColorGradingComponentConfig::m_smhHighlightsColor, "SMH Highlights Color", "SMH Highlights Color") + + ->ClassElement(AZ::Edit::ClassElements::Group, "Final Adjustment") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_colorGradingHueShift, "Hue Shift", "Hue Shift Value") + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 1.0f) + ->DataElement(AZ::Edit::UIHandlers::Slider, &HDRColorGradingComponentConfig::m_colorGradingPostSaturation, "Post Saturation", "Post Saturation Value") + ->Attribute(Edit::Attributes::Min, -100.0f) + ->Attribute(Edit::Attributes::Max, 100.0f) + ; + } + } + } + + EditorHDRColorGradingComponent::EditorHDRColorGradingComponent(const HDRColorGradingComponentConfig& config) + : BaseClass(config) + { + } + + u32 EditorHDRColorGradingComponent::OnConfigurationChanged() + { + m_controller.OnConfigChanged(); + return Edit::PropertyRefreshLevels::AttributesAndValues; + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.h new file mode 100644 index 0000000000..8eea568e3a --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace AZ +{ + namespace Render + { + class EditorHDRColorGradingComponent final + : public AzToolsFramework::Components:: + EditorComponentAdapter + { + public: + using BaseClass = AzToolsFramework::Components::EditorComponentAdapter; + AZ_EDITOR_COMPONENT(AZ::Render::EditorHDRColorGradingComponent, "{C1FAB0B1-5847-4533-B08E-7314AC807B8E}", BaseClass); + + static void Reflect(AZ::ReflectContext* context); + + EditorHDRColorGradingComponent() = default; + EditorHDRColorGradingComponent(const HDRColorGradingComponentConfig& config); + + //! EditorRenderComponentAdapter overrides... + AZ::u32 OnConfigurationChanged() override; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponent.cpp new file mode 100644 index 0000000000..d91a91a09f --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponent.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +namespace AZ +{ + namespace Render + { + HDRColorGradingComponent::HDRColorGradingComponent(const HDRColorGradingComponentConfig& config) + : BaseClass(config) + { + } + + void HDRColorGradingComponent::Reflect(AZ::ReflectContext* context) + { + BaseClass::Reflect(context); + + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class(); + } + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponent.h new file mode 100644 index 0000000000..6c4bd1418f --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponent.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class HDRColorGradingComponent final + : public AzFramework::Components::ComponentAdapter + { + public: + using BaseClass = AzFramework::Components::ComponentAdapter; + AZ_COMPONENT(AZ::Render::HDRColorGradingComponent, "{51968E0F-4DF0-4851-8405-388CBB15B573}", BaseClass); + + HDRColorGradingComponent() = default; + HDRColorGradingComponent(const HDRColorGradingComponentConfig& config); + + static void Reflect(AZ::ReflectContext* context); + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentConfig.cpp new file mode 100644 index 0000000000..b63fa647a0 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentConfig.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + void HDRColorGradingComponentConfig::Reflect(ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class()->Version(0) + + // Auto-gen serialize context code... +#define SERIALIZE_CLASS HDRColorGradingComponentConfig +#include +#include +#include +#undef SERIALIZE_CLASS + ; + } + } + + void HDRColorGradingComponentConfig::CopySettingsTo (HDRColorGradingSettingsInterface* settings) + { + if (!settings) + { + return; + } + +#define COPY_TARGET settings +#include +#include +#include +#undef COPY_TARGET + } + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentController.cpp new file mode 100644 index 0000000000..c0f70ab145 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentController.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include + +#include + +namespace AZ +{ + namespace Render + { + void HDRColorGradingComponentController::Reflect(ReflectContext* context) + { + HDRColorGradingComponentConfig::Reflect(context); + + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("Configuration", &HDRColorGradingComponentController::m_configuration); + } + + if (auto* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("HDRColorGradingRequestBus") + ->Attribute(AZ::Script::Attributes::Module, "render") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + + // Auto-gen behavior context... +#define PARAM_EVENT_BUS HDRColorGradingRequestBus::Events +#include +#include +#include +#undef PARAM_EVENT_BUS + + ; + } + } + + void HDRColorGradingComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("HDRColorGradingService")); + } + + void HDRColorGradingComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("HDRColorGradingService")); + } + + void HDRColorGradingComponentController::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("PostFXLayerService")); + } + + HDRColorGradingComponentController::HDRColorGradingComponentController(const HDRColorGradingComponentConfig& config) + : m_configuration(config) + { + } + + void HDRColorGradingComponentController::Activate(EntityId entityId) + { + m_entityId = entityId; + + PostProcessFeatureProcessorInterface* fp = + RPI::Scene::GetFeatureProcessorForEntity(m_entityId); + if (fp) + { + m_postProcessInterface = fp->GetOrCreateSettingsInterface(m_entityId); + if (m_postProcessInterface) + { + m_settingsInterface = m_postProcessInterface->GetOrCreateHDRColorGradingSettingsInterface(); + OnConfigChanged(); + } + } + HDRColorGradingRequestBus::Handler::BusConnect(m_entityId); + } + + void HDRColorGradingComponentController::Deactivate() + { + HDRColorGradingRequestBus::Handler::BusDisconnect(m_entityId); + + if (m_postProcessInterface) + { + m_postProcessInterface->RemoveHDRColorGradingSettingsInterface(); + } + + m_postProcessInterface = nullptr; + m_settingsInterface = nullptr; + m_entityId.SetInvalid(); + } + + void HDRColorGradingComponentController::SetConfiguration(const HDRColorGradingComponentConfig& config) + { + m_configuration = config; + OnConfigChanged(); + } + + const HDRColorGradingComponentConfig& HDRColorGradingComponentController::GetConfiguration() const + { + return m_configuration; + } + + void HDRColorGradingComponentController::OnConfigChanged() + { + if (m_settingsInterface) + { + m_configuration.CopySettingsTo(m_settingsInterface); + m_settingsInterface->OnConfigChanged(); + } + } + + // Auto-gen getter/setter function definitions... + // The setter functions will set the values on the Atom settings class, then get the value back + // from the settings class to set the local configuration. This is in case the settings class + // applies some custom logic that results in the set value being different from the input +#define AZ_GFX_COMMON_PARAM(ValueType, Name, MemberName, DefaultValue) \ + ValueType HDRColorGradingComponentController::Get##Name() const \ + { \ + return m_configuration.MemberName; \ + } \ + void HDRColorGradingComponentController::Set##Name(ValueType val) \ + { \ + if (m_settingsInterface) \ + { \ + m_settingsInterface->Set##Name(val); \ + m_settingsInterface->OnConfigChanged(); \ + m_configuration.MemberName = m_settingsInterface->Get##Name(); \ + } \ + else \ + { \ + m_configuration.MemberName = val; \ + } \ + } + +#include +#include +#include + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentController.h new file mode 100644 index 0000000000..d5743dfbf6 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/HDRColorGradingComponentController.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace AZ +{ + namespace Render + { + class HDRColorGradingComponentController final + : public HDRColorGradingRequestBus::Handler + { + public: + friend class EditorHDRColorGradingComponent; + + AZ_TYPE_INFO(AZ::Render::HDRColorGradingComponentController, "{CA1D635C-64E9-42C7-A8E0-36C6B825B15D}"); + static void Reflect(AZ::ReflectContext* context); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + + HDRColorGradingComponentController() = default; + HDRColorGradingComponentController(const HDRColorGradingComponentConfig& config); + + void Activate(EntityId entityId); + void Deactivate(); + void SetConfiguration(const HDRColorGradingComponentConfig& config); + const HDRColorGradingComponentConfig& GetConfiguration() const; + + // Auto-gen function override declarations (functions definitions in .cpp)... +#include +#include +#include + + private: + AZ_DISABLE_COPY(HDRColorGradingComponentController); + + void OnConfigChanged(); + + PostProcessSettingsInterface* m_postProcessInterface = nullptr; + HDRColorGradingSettingsInterface* m_settingsInterface = nullptr; + HDRColorGradingComponentConfig m_configuration; + EntityId m_entityId; + }; + } // namespace Render +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp index 4f4f21ab0f..aa68333108 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp @@ -212,6 +212,9 @@ namespace AZ AZ::Vector3 position = AZ::Vector3::CreateZero(); AZ::TransformBus::EventResult(position, GetEntityId(), &AZ::TransformBus::Events::GetWorldTranslation); + AZ::Quaternion rotationQuaternion = AZ::Quaternion::CreateIdentity(); + AZ::TransformBus::EventResult(rotationQuaternion, GetEntityId(), &AZ::TransformBus::Events::GetWorldRotationQuaternion); + AZ::Matrix3x3 rotationMatrix = AZ::Matrix3x3::CreateFromQuaternion(rotationQuaternion); float scale = 1.0f; AZ::TransformBus::EventResult(scale, GetEntityId(), &AZ::TransformBus::Events::GetLocalUniformScale); @@ -224,9 +227,7 @@ namespace AZ AZ::Vector3 innerExtents(configuration.m_innerWidth, configuration.m_innerLength, configuration.m_innerHeight); innerExtents *= scale; - AZ::Vector3 innerMin(position.GetX() - innerExtents.GetX() / 2, position.GetY() - innerExtents.GetY() / 2, position.GetZ() - innerExtents.GetZ() / 2); - AZ::Vector3 innerMax(position.GetX() + innerExtents.GetX() / 2, position.GetY() + innerExtents.GetY() / 2, position.GetZ() + innerExtents.GetZ() / 2); - debugDisplay.DrawWireBox(innerMin, innerMax); + debugDisplay.DrawWireOBB(position, rotationMatrix.GetBasisX(), rotationMatrix.GetBasisY(), rotationMatrix.GetBasisZ(), innerExtents / 2.0f); } AZ::Aabb EditorReflectionProbeComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake index 099cb378f5..174eb30b31 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake @@ -63,6 +63,8 @@ set(FILES Source/PostProcess/EditorPostFxLayerComponent.h Source/PostProcess/Bloom/EditorBloomComponent.cpp Source/PostProcess/Bloom/EditorBloomComponent.h + Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp + Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.h Source/PostProcess/DepthOfField/EditorDepthOfFieldComponent.cpp Source/PostProcess/DepthOfField/EditorDepthOfFieldComponent.h Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake index e353795d04..63f5aedf33 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_files.cmake @@ -83,6 +83,11 @@ set(FILES Source/PostProcess/Bloom/BloomComponentConfig.cpp Source/PostProcess/Bloom/BloomComponentController.cpp Source/PostProcess/Bloom/BloomComponentController.h + Source/PostProcess/ColorGrading/HDRColorGradingComponent.cpp + Source/PostProcess/ColorGrading/HDRColorGradingComponent.h + Source/PostProcess/ColorGrading/HDRColorGradingComponentConfig.cpp + Source/PostProcess/ColorGrading/HDRColorGradingComponentController.cpp + Source/PostProcess/ColorGrading/HDRColorGradingComponentController.h Source/PostProcess/DepthOfField/DepthOfFieldComponent.cpp Source/PostProcess/DepthOfField/DepthOfFieldComponent.h Source/PostProcess/DepthOfField/DepthOfFieldComponentConfig.cpp diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake index 68bbe52415..133856c900 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_public_files.cmake @@ -39,6 +39,8 @@ set(FILES Include/AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlBus.h Include/AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlComponentConfig.h Include/AtomLyIntegration/CommonFeatures/PostProcess/ExposureControl/ExposureControlComponentConstants.h + Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingBus.h + Include/AtomLyIntegration/CommonFeatures/PostProcess/ColorGrading/HDRColorGradingComponentConfig.h Include/AtomLyIntegration/CommonFeatures/PostProcess/Ssao/SsaoBus.h Include/AtomLyIntegration/CommonFeatures/PostProcess/Ssao/SsaoComponentConfiguration.h Include/AtomLyIntegration/CommonFeatures/PostProcess/LookModification/LookModificationBus.h diff --git a/Gems/AtomLyIntegration/CommonFeatures/gem.json b/Gems/AtomLyIntegration/CommonFeatures/gem.json index 8eceb50932..30738ad14c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/gem.json +++ b/Gems/AtomLyIntegration/CommonFeatures/gem.json @@ -8,7 +8,15 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "Atom_Feature_Common", + "LmbrCentral", + "GradientSignal", + "SurfaceData", + "Atom_Bootstrap", + "Atom_RPI", + "AtomToolsFramework" + ] } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/gem.json b/Gems/AtomLyIntegration/EMotionFXAtom/gem.json index a7c3e96d2b..f921990360 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/gem.json +++ b/Gems/AtomLyIntegration/EMotionFXAtom/gem.json @@ -8,7 +8,14 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "EMotionFX", + "Atom", + "Atom_Feature_Common", + "Atom_RPI", + "Atom_RHI", + "CommonFeaturesAtom" + ] } diff --git a/Gems/AtomLyIntegration/ImguiAtom/gem.json b/Gems/AtomLyIntegration/ImguiAtom/gem.json index 918414f053..6b032275b9 100644 --- a/Gems/AtomLyIntegration/ImguiAtom/gem.json +++ b/Gems/AtomLyIntegration/ImguiAtom/gem.json @@ -8,7 +8,10 @@ "canonical_tags": [ "Gem" ], - "user_tags": [ - ], - "requirements": "" + "user_tags": [], + "requirements": "", + "dependencies": [ + "ImGui", + "Atom_Feature_Common" + ] } diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json index f978d777cf..1cee5c8298 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json @@ -10,9 +10,10 @@ ], "user_tags": [ "DCC", - "Digital", - "Content", - "Creation" + "Digital", + "Content", + "Creation" ], - "requirements": "" + "requirements": "", + "dependencies": [] } diff --git a/Gems/AtomLyIntegration/gem.json b/Gems/AtomLyIntegration/gem.json index 5af133c8e8..00b3a25f74 100644 --- a/Gems/AtomLyIntegration/gem.json +++ b/Gems/AtomLyIntegration/gem.json @@ -5,8 +5,24 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Atom O3DE Integration Gem provides components, libraries, and functionality to support and integrate Atom Renderer in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Core", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Core", + "Utility" + ], "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom-ly-integration/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/atom/atom-ly-integration/", + "dependencies": [ + "Atom_AtomBridge", + "AtomFont", + "AtomImGuiTools", + "AtomViewportDisplayIcons", + "AtomViewportDisplayInfo", + "CommonFeaturesAtom", + "EMotionFX_Atom", + "ImguiAtom" + ] } diff --git a/Gems/AudioEngineWwise/gem.json b/Gems/AudioEngineWwise/gem.json index 084c924977..0588af1908 100644 --- a/Gems/AudioEngineWwise/gem.json +++ b/Gems/AudioEngineWwise/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Wwise Audio Engine Gem provides support for Audiokinetic Wave Works Interactive Sound Engine (Wwise).", - "canonical_tags": ["Gem"], - "user_tags": ["Audio", "Utility", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Audio", + "Utility", + "Tools" + ], "icon_path": "preview.png", "requirements": "Users will need to download Wwise from the Audiokinetic Web Site.", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/wwise/audio-engine-wwise/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/wwise/audio-engine-wwise/", + "dependencies": [ + "AudioSystem" + ] } diff --git a/Gems/AudioSystem/gem.json b/Gems/AudioSystem/gem.json index f618103803..64d4d9af5a 100644 --- a/Gems/AudioSystem/gem.json +++ b/Gems/AudioSystem/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Audio System Gem provides the Audio Translation Layer (ATL) and Audio Controls Editor, which add support for audio in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Audio", "Utility", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Audio", + "Utility", + "Tools" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/audio-system/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/audio-system/", + "dependencies": [ + "LmbrCentral" + ] } diff --git a/Gems/BarrierInput/gem.json b/Gems/BarrierInput/gem.json index 738d644d84..7fdc58e8b3 100644 --- a/Gems/BarrierInput/gem.json +++ b/Gems/BarrierInput/gem.json @@ -5,8 +5,17 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Barrier Input Gem allows the Open 3D Engine to function as a Barrier client so that it can receive input from a remote Barrier server.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Barrier", "Synergy"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Barrier", + "Synergy" + ], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "dependencies": [ + "Atom_RPI" + ] } diff --git a/Gems/Blast/gem.json b/Gems/Blast/gem.json index 3b1205d6fe..d6af47f482 100644 --- a/Gems/Blast/gem.json +++ b/Gems/Blast/gem.json @@ -5,9 +5,22 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The NVIDIA Blast Gem provides tools to author fractured mesh assets in Houdini, and functionality to create realistic destruction simulations in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "Animation"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "Animation" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/nvidia-blast/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/nvidia-blast/", + "dependencies": [ + "Atom_Feature_Common", + "CommonFeaturesAtom", + "PhysX", + "Atom_RPI", + "PythonAssetBuilder" + ] } diff --git a/Gems/Camera/Code/CMakeLists.txt b/Gems/Camera/Code/CMakeLists.txt index 138f0fda85..c9f0bf768f 100644 --- a/Gems/Camera/Code/CMakeLists.txt +++ b/Gems/Camera/Code/CMakeLists.txt @@ -18,8 +18,8 @@ ly_add_target( PUBLIC Gem::Atom_RPI.Public AZ::AtomCore - PRIVATE - Legacy::CryCommon + AZ::AzCore + AZ::AzFramework ) ly_add_target( @@ -32,7 +32,6 @@ ly_add_target( Source BUILD_DEPENDENCIES PRIVATE - Legacy::CryCommon Gem::Camera.Static ) @@ -56,11 +55,9 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Source BUILD_DEPENDENCIES PRIVATE - Legacy::CryCommon - Legacy::Editor.Headers - Legacy::EditorCommon AZ::AzToolsFramework Gem::Camera.Static + Gem::AtomToolsFramework.Static RUNTIME_DEPENDENCIES Legacy::EditorCommon ) diff --git a/Gems/Camera/Code/Source/CameraComponent.cpp b/Gems/Camera/Code/Source/CameraComponent.cpp index 0b892b0367..960f13a082 100644 --- a/Gems/Camera/Code/Source/CameraComponent.cpp +++ b/Gems/Camera/Code/Source/CameraComponent.cpp @@ -14,9 +14,7 @@ #include "CameraComponent.h" -#include #include -#include #include namespace Camera diff --git a/Gems/Camera/Code/Source/CameraComponent.h b/Gems/Camera/Code/Source/CameraComponent.h index de6e98154d..b9c94d482f 100644 --- a/Gems/Camera/Code/Source/CameraComponent.h +++ b/Gems/Camera/Code/Source/CameraComponent.h @@ -11,9 +11,6 @@ #include #include -#include -#include -#include #include #include diff --git a/Gems/Camera/Code/Source/CameraComponentController.cpp b/Gems/Camera/Code/Source/CameraComponentController.cpp index a8b5b8992e..b8e5738145 100644 --- a/Gems/Camera/Code/Source/CameraComponentController.cpp +++ b/Gems/Camera/Code/Source/CameraComponentController.cpp @@ -9,7 +9,6 @@ #include "CameraComponentController.h" #include "CameraViewRegistrationBus.h" -#include #include #include #include @@ -53,7 +52,7 @@ namespace Camera ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_fov, "Field of view", "Vertical field of view in degrees") - ->Attribute(AZ::Edit::Attributes::Min, MIN_FOV) + ->Attribute(AZ::Edit::Attributes::Min, MinFoV) ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") ->Attribute(AZ::Edit::Attributes::Step, 1.f) ->Attribute(AZ::Edit::Attributes::Max, AZ::RadToDeg(AZ::Constants::Pi) - 0.0001f) //We assert at fovs >= Pi so set the max for this field to be just under that @@ -61,7 +60,7 @@ namespace Camera ->Attribute(AZ::Edit::Attributes::Visibility, &CameraComponentConfig::GetPerspectiveParameterVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_nearClipDistance, "Near clip distance", "Distance to the near clip plane of the view Frustum") - ->Attribute(AZ::Edit::Attributes::Min, CAMERA_MIN_NEAR) + ->Attribute(AZ::Edit::Attributes::Min, MinimumNearPlaneDistance) ->Attribute(AZ::Edit::Attributes::Suffix, " m") ->Attribute(AZ::Edit::Attributes::Step, 0.1f) ->Attribute(AZ::Edit::Attributes::Max, &CameraComponentConfig::GetFarClipDistance) @@ -150,6 +149,11 @@ namespace Camera } } + void CameraComponentController::SetShouldActivateFunction(AZStd::function shouldActivateFunction) + { + m_shouldActivateFn = shouldActivateFunction; + } + void CameraComponentController::Reflect(AZ::ReflectContext* context) { CameraComponentConfig::Reflect(context); @@ -202,36 +206,6 @@ namespace Camera { m_entityId = entityId; - if ((!m_viewSystem)||(!m_system)) - { - // perform first-time init - if (gEnv) - { - m_system = gEnv->pSystem; - } - if (m_system) - { - // Initialize local view. - m_viewSystem = m_system->GetIViewSystem(); - if (!m_viewSystem) - { - AZ_Error("CameraComponent", m_viewSystem != nullptr, "The CameraComponent shouldn't be used without a local view system"); - } - } - } - - if (m_viewSystem) - { - if (m_view == nullptr) - { - m_view = m_viewSystem->CreateView(); - - AZ::Entity* entity = nullptr; - AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, m_entityId); - m_view->LinkTo(entity); - } - } - auto atomViewportRequests = AZ::Interface::Get(); if (atomViewportRequests) { @@ -270,9 +244,8 @@ namespace Camera CameraBus::Handler::BusConnect(); CameraNotificationBus::Broadcast(&CameraNotificationBus::Events::OnCameraAdded, m_entityId); - // Activate our camera if we're running from the launcher or Editor game mode - // Otherwise, let the Editor keep managing the active camera - if (m_config.m_makeActiveViewOnActivation && (!gEnv || !gEnv->IsEditor() || gEnv->IsEditorGameMode())) + // Only activate if we're configured to do so, and our activation call back indicates that we should + if (m_config.m_makeActiveViewOnActivation && (!m_shouldActivateFn || m_shouldActivateFn())) { MakeActiveView(); } @@ -284,20 +257,6 @@ namespace Camera CameraBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::Handler::BusDisconnect(m_entityId); CameraRequestBus::Handler::BusDisconnect(m_entityId); - if (m_viewSystem) - { - if (m_view != nullptr && m_viewSystem->GetViewId(m_view) != 0) - { - m_view->Unlink(); - } - if (m_viewSystem->GetActiveView() == m_view) - { - m_viewSystem->SetActiveView(m_prevViewId); - } - m_viewSystem->RemoveView(m_view); - m_prevViewId = 0; - m_view = nullptr; - } auto atomViewportRequests = AZ::Interface::Get(); if (atomViewportRequests) @@ -429,13 +388,6 @@ namespace Camera return; } - // Set Legacy Cry view, if it exists - if (m_viewSystem) - { - m_prevViewId = AZ::u32(m_viewSystem->GetActiveViewId()); - m_viewSystem->SetActiveView(m_view); - } - // Set Atom camera, if it exists if (m_atomCamera) { @@ -461,12 +413,6 @@ namespace Camera return; } - if (m_view) - { - CCamera& camera = m_view->GetCamera(); - camera.SetMatrix(AZTransformToLYTransform(world.GetOrthogonalized())); - } - if (m_atomCamera) { m_updatingTransformFromEntity = true; @@ -495,25 +441,15 @@ namespace Camera void CameraComponentController::UpdateCamera() { - if (m_view) - { - auto viewParams = *m_view->GetCurrentParams(); - viewParams.fov = AZ::DegToRad(m_config.m_fov); - viewParams.nearplane = m_config.m_nearClipDistance; - viewParams.farplane = m_config.m_farClipDistance; - m_view->SetCurrentParams(viewParams); - } - if (auto viewportContext = GetViewportContext()) { AZ::Matrix4x4 viewToClipMatrix; - float aspectRatio = m_view ? m_view->GetCamera().GetPixelAspectRatio() : 1.f; if (!m_atomAuxGeom) { SetupAtomAuxGeom(viewportContext); } auto windowSize = viewportContext->GetViewportSize(); - aspectRatio = aznumeric_cast(windowSize.m_width) / aznumeric_cast(windowSize.m_height); + const float aspectRatio = aznumeric_cast(windowSize.m_width) / aznumeric_cast(windowSize.m_height); // This assumes a reversed depth buffer, in line with other LY Atom integration if (m_config.m_orthographic) diff --git a/Gems/Camera/Code/Source/CameraComponentController.h b/Gems/Camera/Code/Source/CameraComponentController.h index 923da65618..5eca6b1711 100644 --- a/Gems/Camera/Code/Source/CameraComponentController.h +++ b/Gems/Camera/Code/Source/CameraComponentController.h @@ -16,15 +16,12 @@ #include #include -#include -#include -#include - namespace Camera { static constexpr float DefaultFoV = 75.0f; static constexpr float MinFoV = std::numeric_limits::epsilon(); static constexpr float MaxFoV = AZ::RadToDeg(AZ::Constants::Pi); + static constexpr float MinimumNearPlaneDistance = 0.001f; static constexpr float DefaultNearPlaneDistance = 0.2f; static constexpr float DefaultFarClipPlaneDistance = 1024.0f; static constexpr float DefaultFrustumDimension = 256.f; @@ -69,6 +66,10 @@ namespace Camera CameraComponentController() = default; explicit CameraComponentController(const CameraComponentConfig& config); + //! Defines a callback for determining whether this camera should push itself to the top of the Atom camera stack. + //! Used by the Editor to disable undesirable camera changes in edit mode. + void SetShouldActivateFunction(AZStd::function shouldActivateFunction); + // Controller interface static void Reflect(AZ::ReflectContext* context); static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); @@ -134,10 +135,6 @@ namespace Camera bool m_updatingTransformFromEntity = false; bool m_isActiveView = false; - // Cry view integration - IView* m_view = nullptr; - AZ::u32 m_prevViewId = 0; - IViewSystem* m_viewSystem = nullptr; - ISystem* m_system = nullptr; + AZStd::function m_shouldActivateFn; }; } // namespace Camera diff --git a/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp b/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp index bf3083bf59..00a147c17a 100644 --- a/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp +++ b/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp @@ -21,17 +21,12 @@ #include #include -#include -#include -#include -#include -#include - -#include -#include #include #include "ViewportCameraSelectorWindow.h" +#include +#include + namespace Camera { void CameraEditorSystemComponent::Reflect(AZ::ReflectContext* context) @@ -72,15 +67,6 @@ namespace Camera void CameraEditorSystemComponent::PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2&, int flags) { - IEditor* editor; - AzToolsFramework::EditorRequests::Bus::BroadcastResult(editor, &AzToolsFramework::EditorRequests::GetEditor); - - CGameEngine* gameEngine = editor->GetGameEngine(); - if (!gameEngine || !gameEngine->IsLevelLoaded()) - { - return; - } - if (!(flags & AzToolsFramework::EditorEvents::eECMF_HIDE_ENTITY_CREATION)) { QAction* action = menu->addAction(QObject::tr("Create camera entity from view")); @@ -90,9 +76,6 @@ namespace Camera void CameraEditorSystemComponent::CreateCameraEntityFromViewport() { - IEditor* editor = nullptr; - AzToolsFramework::EditorRequests::Bus::BroadcastResult(editor, &AzToolsFramework::EditorRequests::GetEditor); - AzFramework::CameraState cameraState{}; AZ::EBusReduceResult> aggregator; Camera::EditorCameraRequestBus::BroadcastResult( diff --git a/Gems/Camera/Code/Source/CameraGem.cpp b/Gems/Camera/Code/Source/CameraGem.cpp index 7efb09a59c..99f270e71b 100644 --- a/Gems/Camera/Code/Source/CameraGem.cpp +++ b/Gems/Camera/Code/Source/CameraGem.cpp @@ -7,7 +7,6 @@ */ #include -#include #include "CameraComponent.h" #include "CameraSystemComponent.h" @@ -22,13 +21,13 @@ namespace Camera { class CameraModule - : public CryHooksModule + : public AZ::Module { public: AZ_RTTI(CameraModule, "{C2E72B0D-BCEF-452A-9BFA-03833015258C}", AZ::Module); CameraModule() - : CryHooksModule() + : AZ::Module() { m_descriptors.insert(m_descriptors.end(), { Camera::CameraComponent::CreateDescriptor(), diff --git a/Gems/Camera/Code/Source/EditorCameraComponent.cpp b/Gems/Camera/Code/Source/EditorCameraComponent.cpp index 4ebec334ec..3ac60cbff8 100644 --- a/Gems/Camera/Code/Source/EditorCameraComponent.cpp +++ b/Gems/Camera/Code/Source/EditorCameraComponent.cpp @@ -12,10 +12,9 @@ #include "EditorCameraComponent.h" #include "ViewportCameraSelectorWindow.h" -#include -#include #include #include +#include #include #include @@ -33,6 +32,14 @@ namespace Camera CameraComponentConfig controllerConfig = m_controller.GetConfiguration(); controllerConfig.m_editorEntityId = GetEntityId().operator AZ::u64(); m_controller.SetConfiguration(controllerConfig); + // Only allow our camera to activate with the component if we're currently in game mode. + m_controller.SetShouldActivateFunction([]() + { + bool isInGameMode = true; + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( + isInGameMode, &AzToolsFramework::EditorEntityContextRequestBus::Events::IsEditorRunningGame); + return isInGameMode; + }); // Call base class activate, which in turn calls Activate on our controller. EditorCameraComponentBase::Activate(); diff --git a/Gems/Camera/Code/Source/EditorCameraComponent.h b/Gems/Camera/Code/Source/EditorCameraComponent.h index 02b9d591d0..0cb130e7a0 100644 --- a/Gems/Camera/Code/Source/EditorCameraComponent.h +++ b/Gems/Camera/Code/Source/EditorCameraComponent.h @@ -21,8 +21,6 @@ #include #include "CameraComponent.h" #include "CameraComponentController.h" -#include -#include #include diff --git a/Gems/Camera/Code/Source/ViewportCameraSelectorWindow.cpp b/Gems/Camera/Code/Source/ViewportCameraSelectorWindow.cpp index 7a8d6be020..dc091db9e5 100644 --- a/Gems/Camera/Code/Source/ViewportCameraSelectorWindow.cpp +++ b/Gems/Camera/Code/Source/ViewportCameraSelectorWindow.cpp @@ -7,17 +7,14 @@ */ #include "ViewportCameraSelectorWindow.h" #include "ViewportCameraSelectorWindow_Internals.h" -#include #include #include #include #include #include #include -#include -#include -#include #include +#include namespace Qt { @@ -308,7 +305,7 @@ namespace Camera void RegisterViewportCameraSelectorWindow() { - QtViewOptions viewOptions; + AzToolsFramework::ViewPaneOptions viewOptions; viewOptions.isPreview = true; viewOptions.showInMenu = true; viewOptions.preferedDockingArea = Qt::DockWidgetArea::LeftDockWidgetArea; diff --git a/Gems/Camera/gem.json b/Gems/Camera/gem.json index 2fc2ea8355..9f8ea22412 100644 --- a/Gems/Camera/gem.json +++ b/Gems/Camera/gem.json @@ -5,9 +5,17 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Camera Gem provides a basic camera component that defines a frustum for runtime rendering.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/camera/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/camera/", + "dependencies": [ + "Atom_RPI" + ] } diff --git a/Gems/CameraFramework/gem.json b/Gems/CameraFramework/gem.json index 2ab25a84b4..c5520fd5b4 100644 --- a/Gems/CameraFramework/gem.json +++ b/Gems/CameraFramework/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Camera Framework Gem provides a base for implementing more complex camera systems.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Framework", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Framework", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/camera-framework/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/camera-framework/", + "dependencies": [] } diff --git a/Gems/CertificateManager/gem.json b/Gems/CertificateManager/gem.json index cb49abc6b6..968da2788a 100644 --- a/Gems/CertificateManager/gem.json +++ b/Gems/CertificateManager/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Certificate Manager Gem provides access to authentication files for secure game connections from Amazon S3, files on disk, and other 3rd party sources.", - "canonical_tags": ["Gem"], - "user_tags": ["Network", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Network", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/certificate-manager/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/certificate-manager/", + "dependencies": [] } diff --git a/Gems/CrashReporting/gem.json b/Gems/CrashReporting/gem.json index bff54dcf36..9b8d3a9e0a 100644 --- a/Gems/CrashReporting/gem.json +++ b/Gems/CrashReporting/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Crash Reporting Gem provides support for external crash reporting for Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/crash-reporting/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/crash-reporting/", + "dependencies": [] } diff --git a/Gems/CustomAssetExample/gem.json b/Gems/CustomAssetExample/gem.json index ac7c2788d7..ab5ed7002d 100644 --- a/Gems/CustomAssetExample/gem.json +++ b/Gems/CustomAssetExample/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Custom Asset Example Gem provides example code for creating a custom asset for Open 3D Engine's asset pipeline.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Tools" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/custom-asset-example/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/custom-asset-example/", + "dependencies": [] } diff --git a/Gems/DebugDraw/gem.json b/Gems/DebugDraw/gem.json index a7939f4077..7e0da07103 100644 --- a/Gems/DebugDraw/gem.json +++ b/Gems/DebugDraw/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Debug Draw Gem provides Editor and runtime debug visualization features for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/debug-draw/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/debug-draw/", + "dependencies": [ + "Atom_RPI", + "Atom_Bootstrap" + ] } diff --git a/Gems/DevTextures/gem.json b/Gems/DevTextures/gem.json index 81d14e78cb..8b40badbf1 100644 --- a/Gems/DevTextures/gem.json +++ b/Gems/DevTextures/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Dev Textures Gem provides a collection of general purpose texture assets useful for prototypes and preproduction.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Debug", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Debug", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/dev-textures/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/dev-textures/", + "dependencies": [] } diff --git a/Gems/EMotionFX/Assets/Editor/Layouts/SimulatedObjects.layout b/Gems/EMotionFX/Assets/Editor/Layouts/SimulatedObjects.layout index a7879584a9..f52df642e8 100644 Binary files a/Gems/EMotionFX/Assets/Editor/Layouts/SimulatedObjects.layout and b/Gems/EMotionFX/Assets/Editor/Layouts/SimulatedObjects.layout differ diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp index 5a4ca95670..b547b92306 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp @@ -372,6 +372,8 @@ namespace EMotionFX // updates the skinning matrices of all nodes void ActorInstance::UpdateSkinningMatrices() { + AZ_PROFILE_SCOPE(Animation, "ActorInstance::UpdateSkinningMatrices"); + AZ::Matrix3x4* skinningMatrices = m_transformData->GetSkinningMatrices(); const Pose* pose = m_transformData->GetCurrentPose(); @@ -596,6 +598,8 @@ namespace EMotionFX // update the bounding volume void ActorInstance::UpdateBounds(size_t geomLODLevel, EBoundsType boundsType, uint32 itemFrequency) { + AZ_PROFILE_SCOPE(Animation, "ActorInstance::UpdateBounds"); + // depending on the bounding volume update type switch (boundsType) { diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphInstance.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphInstance.cpp index d0c77721d1..7f9e8ba6fc 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphInstance.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphInstance.cpp @@ -218,6 +218,8 @@ namespace EMotionFX // output the results into the internal pose object void AnimGraphInstance::Output(Pose* outputPose) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::Output"); + // reset max used const uint32 threadIndex = m_actorInstance->GetThreadIndex(); AnimGraphPosePool& posePool = GetEMotionFX().GetThreadData(threadIndex)->GetPosePool(); @@ -854,6 +856,8 @@ namespace EMotionFX // synchronize all nodes, based on sync tracks etc void AnimGraphInstance::Update(float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::Update"); + // pass 0: (Optional, networking only) When this instance is shared between network, restore the instance using an animgraph snapshot. if (m_snapshot) { @@ -940,6 +944,8 @@ namespace EMotionFX // reset all node pose ref counts void AnimGraphInstance::ResetPoseRefCountsForAllNodes() { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::ResetPoseRefCountsForAllNodes"); + const size_t numNodes = m_animGraph->GetNumNodes(); for (size_t i = 0; i < numNodes; ++i) { @@ -951,6 +957,8 @@ namespace EMotionFX // reset all node pose ref counts void AnimGraphInstance::ResetRefDataRefCountsForAllNodes() { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::ResetRefDataRefCountsForAllNodes"); + const size_t numNodes = m_animGraph->GetNumNodes(); for (size_t i = 0; i < numNodes; ++i) { @@ -962,6 +970,8 @@ namespace EMotionFX // reset all node flags void AnimGraphInstance::ResetFlagsForAllObjects() { + AZ_PROFILE_SCOPE(Animation, "AnimGraphInstance::ResetFlagsForAllObjects"); + MCore::MemSet(m_objectFlags.data(), 0, sizeof(uint32) * m_objectFlags.size()); for (AnimGraphInstance* childInstance : m_childAnimGraphInstances) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp index 6bcc69360b..c5e50ed9dd 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphMotionNode.cpp @@ -394,6 +394,8 @@ namespace EMotionFX // the main process method of the final node void AnimGraphMotionNode::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphMotionNode::Output"); + // if this motion is disabled, output the bind pose if (m_disabled) { @@ -537,6 +539,8 @@ namespace EMotionFX void AnimGraphMotionNode::UniqueData::Update() { + AZ_PROFILE_SCOPE(Animation, "AnimGraphMotionNode::Update"); + AnimGraphMotionNode* motionNode = azdynamic_cast(m_object); AZ_Assert(motionNode, "Unique data linked to incorrect node type."); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateMachine.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateMachine.cpp index 9b4c2d07ac..0024d97a45 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateMachine.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphStateMachine.cpp @@ -92,6 +92,8 @@ namespace EMotionFX void AnimGraphStateMachine::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::Update"); + ActorInstance* actorInstance = animGraphInstance->GetActorInstance(); AnimGraphPose* outputPose = nullptr; @@ -476,6 +478,8 @@ namespace EMotionFX void AnimGraphStateMachine::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::Update"); + UniqueData* uniqueData = static_cast(FindOrCreateUniqueNodeData(animGraphInstance)); // Defer switch to entry state. @@ -622,6 +626,8 @@ namespace EMotionFX void AnimGraphStateMachine::PostUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::PostUpdate"); + RequestRefDatas(animGraphInstance); UniqueData* uniqueData = static_cast(FindOrCreateUniqueNodeData(animGraphInstance)); AnimGraphRefCountedData* data = uniqueData->GetRefCountedData(); @@ -1344,6 +1350,8 @@ namespace EMotionFX void AnimGraphStateMachine::TopDownUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::TopDownUpdate"); + UniqueData* uniqueData = static_cast(FindOrCreateUniqueNodeData(animGraphInstance)); if (!IsTransitioning(uniqueData)) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace1DNode.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace1DNode.cpp index 0e823075a4..deeb593c96 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace1DNode.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace1DNode.cpp @@ -166,6 +166,8 @@ namespace EMotionFX void BlendSpace1DNode::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "BlendSpace1DNode::Output"); + if (!AnimGraphInstanceExists(animGraphInstance)) { return; @@ -276,6 +278,8 @@ namespace EMotionFX void BlendSpace1DNode::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendSpace1DNode::Update"); + if (!m_disabled) { EMotionFX::BlendTreeConnection* paramConnection = GetInputPort(INPUTPORT_VALUE).m_connection; diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace2DNode.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace2DNode.cpp index 03a7552410..66366087f8 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace2DNode.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/BlendSpace2DNode.cpp @@ -282,6 +282,8 @@ namespace EMotionFX void BlendSpace2DNode::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "BlendSpace2DNode::Output"); + if (!AnimGraphInstanceExists(animGraphInstance)) { return; @@ -402,6 +404,8 @@ namespace EMotionFX void BlendSpace2DNode::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendSpace2DNode::Update"); + if (!AnimGraphInstanceExists(animGraphInstance)) { return; diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/BlendTree.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/BlendTree.cpp index f742696275..a4109a5a60 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/BlendTree.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/BlendTree.cpp @@ -116,10 +116,11 @@ namespace EMotionFX return nullptr; } - // process the blend tree and calculate its output void BlendTree::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "BlendTree::Output"); + AZ_Assert(m_finalNode, "There should always be a final node. Something seems to be wrong with the blend tree creation."); // get the output pose @@ -164,6 +165,8 @@ namespace EMotionFX // post sync update void BlendTree::PostUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "AnimGraphStateMachine::PostUpdate"); + // if this node is disabled, exit if (m_disabled) { @@ -212,6 +215,8 @@ namespace EMotionFX // update all nodes void BlendTree::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendTree::Update"); + // if this node is disabled, output the bind pose if (m_disabled) { @@ -256,6 +261,8 @@ namespace EMotionFX // top down update void BlendTree::TopDownUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendTree::TopDownUpdate"); + // get the final node AnimGraphNode* finalNode = GetRealFinalNode(); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeBlend2Node.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeBlend2Node.cpp index 06223d4b14..b14f72c7c3 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeBlend2Node.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/BlendTreeBlend2Node.cpp @@ -45,6 +45,8 @@ namespace EMotionFX void BlendTreeBlend2Node::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) { + AZ_PROFILE_SCOPE(Animation, "BlendTreeBlend2Node::Update"); + if (m_disabled) { AnimGraphNodeData* uniqueData = FindOrCreateUniqueNodeData(animGraphInstance); @@ -88,9 +90,10 @@ namespace EMotionFX } } - void BlendTreeBlend2Node::Output(AnimGraphInstance* animGraphInstance) { + AZ_PROFILE_SCOPE(Animation, "BlendTreeBlend2Node::Output"); + if (m_disabled) { RequestPoses(animGraphInstance); diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/MotionSystem.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/MotionSystem.cpp index a6ddb776c2..237389312f 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/MotionSystem.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/MotionSystem.cpp @@ -147,6 +147,8 @@ namespace EMotionFX // update motion queue and instances void MotionSystem::Update(float timePassed, bool updateNodes) { + AZ_PROFILE_SCOPE(Animation, "MotionSystem::Update"); + MCORE_UNUSED(updateNodes); // update the motion queue diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointWidget.cpp index 971eb15501..0597b79d90 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointWidget.cpp @@ -69,7 +69,7 @@ namespace EMotionFX void ClothJointWidget::InternalReinit() { - if (m_selectedModelIndices.size() == 1) + if (GetSelectedModelIndices().size() == 1) { Physics::CharacterColliderNodeConfiguration* nodeConfig = GetNodeConfig(); if (nodeConfig) @@ -94,17 +94,17 @@ namespace EMotionFX void ClothJointWidget::OnAddCollider(const AZ::TypeId& colliderType) { - ColliderHelpers::AddCollider(m_selectedModelIndices , PhysicsSetup::Cloth, colliderType); + ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::Cloth, colliderType); } void ClothJointWidget::OnCopyCollider(size_t colliderIndex) { - ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Cloth); + ColliderHelpers::CopyColliderToClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::Cloth); } void ClothJointWidget::OnPasteCollider(size_t colliderIndex, bool replace) { - ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Cloth, replace); + ColliderHelpers::PasteColliderFromClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::Cloth, replace); } void ClothJointWidget::OnRemoveCollider(size_t colliderIndex) @@ -114,7 +114,7 @@ namespace EMotionFX Physics::CharacterColliderNodeConfiguration* ClothJointWidget::GetNodeConfig() const { - AZ_Assert(m_selectedModelIndices.size() == 1, "Get Node config function only return the config when it is single seleted"); + AZ_Assert(GetSelectedModelIndices().size() == 1, "Get Node config function only return the config when it is single seleted"); Actor* actor = GetActor(); Node* joint = GetNode(); if (!actor || !joint) diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointWidget.cpp index 17265668ab..7fc9ee36e2 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointWidget.cpp @@ -65,7 +65,7 @@ namespace EMotionFX void HitDetectionJointWidget::InternalReinit() { - if (m_selectedModelIndices.size() == 1) + if (GetSelectedModelIndices().size() == 1) { Physics::CharacterColliderNodeConfiguration* hitDetectionNodeConfig = GetNodeConfig(); if (hitDetectionNodeConfig) @@ -90,17 +90,17 @@ namespace EMotionFX void HitDetectionJointWidget::OnAddCollider(const AZ::TypeId& colliderType) { - ColliderHelpers::AddCollider(m_selectedModelIndices, PhysicsSetup::HitDetection, colliderType); + ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::HitDetection, colliderType); } void HitDetectionJointWidget::OnCopyCollider(size_t colliderIndex) { - ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::HitDetection); + ColliderHelpers::CopyColliderToClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::HitDetection); } void HitDetectionJointWidget::OnPasteCollider(size_t colliderIndex, bool replace) { - ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::HitDetection, replace); + ColliderHelpers::PasteColliderFromClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::HitDetection, replace); } void HitDetectionJointWidget::OnRemoveCollider(size_t colliderIndex) @@ -110,7 +110,7 @@ namespace EMotionFX Physics::CharacterColliderNodeConfiguration* HitDetectionJointWidget::GetNodeConfig() { - AZ_Assert(m_selectedModelIndices.size() == 1, "Get Node config function only return the config when it is single seleted"); + AZ_Assert(GetSelectedModelIndices().size() == 1, "Get Node config function only return the config when it is single seleted"); Actor* actor = GetActor(); Node* node = GetNode(); if (!actor || !node) diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeWidget.cpp index 00cdb90a22..29745beb68 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeWidget.cpp @@ -116,7 +116,8 @@ namespace EMotionFX void RagdollNodeWidget::InternalReinit() { - if (m_selectedModelIndices.size() == 1) + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); + if (selectedModelIndices.size() == 1) { m_ragdollNodeEditor->ClearInstances(false); @@ -142,7 +143,7 @@ namespace EMotionFX m_collidersWidget->Reset(); } - m_jointLimitWidget->Update(m_selectedModelIndices[0]); + m_jointLimitWidget->Update(selectedModelIndices[0]); m_ragdollNodeCard->setExpanded(true); m_ragdollNodeCard->show(); m_jointLimitWidget->show(); @@ -169,31 +170,32 @@ namespace EMotionFX void RagdollNodeWidget::OnAddRemoveRagdollNode() { + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); if (GetRagdollNodeConfig()) { // The node is present in the ragdoll, remove it. - RagdollNodeInspectorPlugin::RemoveFromRagdoll(m_selectedModelIndices); + RagdollNodeInspectorPlugin::RemoveFromRagdoll(selectedModelIndices); } else { // The node is not part of the ragdoll, add it. - RagdollNodeInspectorPlugin::AddToRagdoll(m_selectedModelIndices); + RagdollNodeInspectorPlugin::AddToRagdoll(selectedModelIndices); } } void RagdollNodeWidget::OnAddCollider(const AZ::TypeId& colliderType) { - ColliderHelpers::AddCollider(m_selectedModelIndices, PhysicsSetup::Ragdoll, colliderType); + ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::Ragdoll, colliderType); } void RagdollNodeWidget::OnCopyCollider(size_t colliderIndex) { - ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Ragdoll); + ColliderHelpers::CopyColliderToClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::Ragdoll); } void RagdollNodeWidget::OnPasteCollider(size_t colliderIndex, bool replace) { - ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::Ragdoll, replace); + ColliderHelpers::PasteColliderFromClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::Ragdoll, replace); } void RagdollNodeWidget::OnRemoveCollider(size_t colliderIndex) diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectColliderWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectColliderWidget.cpp index 8c6c72934d..b56cafc9be 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectColliderWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectColliderWidget.cpp @@ -131,7 +131,8 @@ namespace EMotionFX void SimulatedObjectColliderWidget::InternalReinit() { - if (m_selectedModelIndices.size() == 1) + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); + if (selectedModelIndices.size() == 1) { Physics::CharacterColliderNodeConfiguration* nodeConfig = GetNodeConfig(); if (nodeConfig) @@ -172,12 +173,13 @@ namespace EMotionFX } AZStd::string labelText; + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); const AZStd::vector& simObjs = actor->GetSimulatedObjectSetup()->GetSimulatedObjects(); for (const SimulatedObject* obj : simObjs) { - for (int i = 0; i < m_selectedModelIndices.size(); ++i) + for (int i = 0; i < selectedModelIndices.size(); ++i) { - Node* node = m_selectedModelIndices[i].data(SkeletonModel::ROLE_POINTER).value(); + Node* node = selectedModelIndices[i].data(SkeletonModel::ROLE_POINTER).value(); if (obj->FindSimulatedJointBySkeletonJointIndex(node->GetNodeIndex())) { if (!labelText.empty()) @@ -208,8 +210,9 @@ namespace EMotionFX return; } + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); // Only show the notification when it is single selection. - if (m_selectedModelIndices.size() != 1) + if (selectedModelIndices.size() != 1) { return; } @@ -250,17 +253,18 @@ namespace EMotionFX void SimulatedObjectColliderWidget::OnAddCollider(const AZ::TypeId& colliderType) { - ColliderHelpers::AddCollider(m_selectedModelIndices, PhysicsSetup::SimulatedObjectCollider, colliderType); + ColliderHelpers::AddCollider(GetSelectedModelIndices(), PhysicsSetup::SimulatedObjectCollider, colliderType); } void SimulatedObjectColliderWidget::OnCopyCollider(size_t colliderIndex) { - ColliderHelpers::CopyColliderToClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider); + ColliderHelpers::CopyColliderToClipboard(GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider); } void SimulatedObjectColliderWidget::OnPasteCollider(size_t colliderIndex, bool replace) { - ColliderHelpers::PasteColliderFromClipboard(m_selectedModelIndices.first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider, replace); + ColliderHelpers::PasteColliderFromClipboard( + GetSelectedModelIndices().first(), colliderIndex, PhysicsSetup::SimulatedObjectCollider, replace); } void SimulatedObjectColliderWidget::OnRemoveCollider(size_t colliderIndex) @@ -270,7 +274,7 @@ namespace EMotionFX Physics::CharacterColliderNodeConfiguration* SimulatedObjectColliderWidget::GetNodeConfig() const { - AZ_Assert(m_selectedModelIndices.size() == 1, "Get Node config function only return the config when it is single seleted"); + AZ_Assert(GetSelectedModelIndices().size() == 1, "Get Node config function only return the config when it is single seleted"); Actor* actor = GetActor(); Node* joint = GetNode(); if (!actor || !joint) diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp index f8ca291d55..ec8d1147f2 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp @@ -211,15 +211,15 @@ namespace EMotionFX AZ::Outcome SkeletonOutlinerPlugin::GetSelectedRowIndices() { - return AZ::Success(m_selectedRows); + return AZ::Success(m_treeView->selectionModel()->selectedRows()); } void SkeletonOutlinerPlugin::OnSelectionChanged([[maybe_unused]] const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected) { - m_selectedRows = m_treeView->selectionModel()->selectedRows(); - if (m_selectedRows.size() == 1) + QModelIndexList selectedRows = m_treeView->selectionModel()->selectedRows(); + if (selectedRows.size() == 1) { - const QModelIndex& modelIndex = m_selectedRows[0]; + const QModelIndex& modelIndex = selectedRows[0]; Node* selectedNode = modelIndex.data(SkeletonModel::ROLE_POINTER).value(); Actor* selectedActor = modelIndex.data(SkeletonModel::ROLE_ACTOR_POINTER).value(); SkeletonOutlinerNotificationBus::Broadcast(&SkeletonOutlinerNotifications::SingleNodeSelectionChanged, selectedActor, selectedNode); diff --git a/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.cpp index e9eeb7530d..9cfc763fc6 100644 --- a/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.cpp @@ -72,14 +72,7 @@ namespace EMotionFX setLayout(mainLayout); - AZ::Outcome selectedRowIndicesOutcome; - QModelIndexList selectedModelIndices; - SkeletonOutlinerRequestBus::BroadcastResult(selectedRowIndicesOutcome, &SkeletonOutlinerRequests::GetSelectedRowIndices); - if (selectedRowIndicesOutcome.IsSuccess()) - { - selectedModelIndices = selectedRowIndicesOutcome.GetValue(); - } - Reinit(selectedModelIndices); + Reinit(); // Connect to the model. SkeletonModel* skeletonModel = nullptr; @@ -92,9 +85,9 @@ namespace EMotionFX } } - void SkeletonModelJointWidget::Reinit(const QModelIndexList& selectedModelIndices) + void SkeletonModelJointWidget::Reinit() { - m_selectedModelIndices = selectedModelIndices; + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); if (!EMStudio::GetManager()->GetIgnoreVisibility() && !isVisible()) { @@ -103,15 +96,15 @@ namespace EMotionFX if (GetActor()) { - if (!m_selectedModelIndices.isEmpty()) + if (!selectedModelIndices.isEmpty()) { - if (m_selectedModelIndices.size() == 1) + if (selectedModelIndices.size() == 1) { m_jointNameLabel->setText(GetNode()->GetName()); } else { - m_jointNameLabel->setText(QString("%1 joints selected").arg(m_selectedModelIndices.size())); + m_jointNameLabel->setText(QString("%1 joints selected").arg(selectedModelIndices.size())); } m_noSelectionWidget->hide(); @@ -136,7 +129,7 @@ namespace EMotionFX void SkeletonModelJointWidget::showEvent(QShowEvent* event) { QWidget::showEvent(event); - Reinit(m_selectedModelIndices); + Reinit(); } void SkeletonModelJointWidget::OnSelectionChanged([[maybe_unused]] const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected) @@ -146,36 +139,28 @@ namespace EMotionFX if (skeletonModel) { const QModelIndexList selectedRows = skeletonModel->GetSelectionModel().selectedRows(); - Reinit(selectedRows); } + Reinit(); } void SkeletonModelJointWidget::OnDataChanged([[maybe_unused]] const QModelIndex& topLeft, [[maybe_unused]] const QModelIndex& bottomRight, [[maybe_unused]] const QVector& roles) { - Reinit(m_selectedModelIndices); + Reinit(); } void SkeletonModelJointWidget::OnModelReset() { - Reinit(QModelIndexList()); + Reinit(); } Actor* SkeletonModelJointWidget::GetActor() const { Actor* actor = nullptr; - if (!m_selectedModelIndices.empty()) + SkeletonModel* skeletonModel = nullptr; + SkeletonOutlinerRequestBus::BroadcastResult(skeletonModel, &SkeletonOutlinerRequests::GetModel); + if (skeletonModel) { - actor = m_selectedModelIndices[0].data(SkeletonModel::ROLE_ACTOR_POINTER).value(); - } - - if (!actor) - { - SkeletonModel* skeletonModel = nullptr; - SkeletonOutlinerRequestBus::BroadcastResult(skeletonModel, &SkeletonOutlinerRequests::GetModel); - if (skeletonModel) - { - actor = skeletonModel->GetActor(); - } + actor = skeletonModel->GetActor(); } return actor; } @@ -183,10 +168,24 @@ namespace EMotionFX Node* SkeletonModelJointWidget::GetNode() const { Node* node = nullptr; - if (!m_selectedModelIndices.empty()) + const QModelIndexList& selectedModelIndices = GetSelectedModelIndices(); + if (!selectedModelIndices.empty()) { - node = m_selectedModelIndices[0].data(SkeletonModel::ROLE_POINTER).value(); + node = selectedModelIndices[0].data(SkeletonModel::ROLE_POINTER).value(); } return node; } + + QModelIndexList SkeletonModelJointWidget::GetSelectedModelIndices() const + { + QModelIndexList selectedModelIndices; + SkeletonModel* skeletonModel = nullptr; + SkeletonOutlinerRequestBus::BroadcastResult(skeletonModel, &SkeletonOutlinerRequests::GetModel); + if (skeletonModel) + { + selectedModelIndices = skeletonModel->GetSelectionModel().selectedRows(); + } + + return selectedModelIndices; + } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.h b/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.h index 52178b4b55..399615c42a 100644 --- a/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.h +++ b/Gems/EMotionFX/Code/Source/Editor/SkeletonModelJointWidget.h @@ -33,13 +33,14 @@ namespace EMotionFX virtual void CreateGUI(); - void Reinit(const QModelIndexList& selectedModelIndices); + void Reinit(); void showEvent(QShowEvent* event) override; protected: Actor* GetActor() const; Node* GetNode() const; + QModelIndexList GetSelectedModelIndices() const; virtual QWidget* CreateContentWidget(QWidget* parent) = 0; virtual QWidget* CreateNoSelectionWidget(QWidget* parent) = 0; virtual void InternalReinit() = 0; @@ -50,7 +51,6 @@ namespace EMotionFX void OnModelReset(); protected: - QModelIndexList m_selectedModelIndices; QLabel* m_jointNameLabel; static int s_jointLabelSpacing; static int s_jointNameSpacing; diff --git a/Gems/EMotionFX/gem.json b/Gems/EMotionFX/gem.json index f00803ec2c..f1734d854d 100644 --- a/Gems/EMotionFX/gem.json +++ b/Gems/EMotionFX/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The EMotion FX Animation Gem provides Open 3D Engine's animation system for rigged actors and includes Animation Editor, a tool for creating animated behaviors, simulated objects, and colliders for rigged actors.", - "canonical_tags": ["Gem"], - "user_tags": ["Animation", "Tools", "Simulation"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Animation", + "Tools", + "Simulation" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/animation/emotionfx/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/animation/emotionfx/", + "dependencies": [ + "Atom_RPI", + "LmbrCentral" + ] } diff --git a/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp b/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp index 38b29bcdf2..f9f9580c1a 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonProxyBus.cpp @@ -195,7 +195,7 @@ namespace EditorPythonBindings if (!m_handler) { - AZ_Error("python", false, "No EBus connection deteced; missing call or failed call to connect()?"); + AZ_Error("python", false, "No EBus connection detected; missing call or failed call to connect()?"); return false; } diff --git a/Gems/EditorPythonBindings/gem.json b/Gems/EditorPythonBindings/gem.json index fae091a63f..13c5800dd7 100644 --- a/Gems/EditorPythonBindings/gem.json +++ b/Gems/EditorPythonBindings/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Editor Python Bindings Gem provides Python commands for Open 3D Engine Editor functions.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/editor-python-bindings/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/editor-python-bindings/", + "dependencies": [] } diff --git a/Gems/ExpressionEvaluation/gem.json b/Gems/ExpressionEvaluation/gem.json index 2f555916a5..6bcc666a4d 100644 --- a/Gems/ExpressionEvaluation/gem.json +++ b/Gems/ExpressionEvaluation/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Expression Evaluation Gem provides a method for parsing and executing string expressions in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/expression-evaluation/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/expression-evaluation/", + "dependencies": [] } diff --git a/Gems/FastNoise/gem.json b/Gems/FastNoise/gem.json index 5ee5bbaf89..ac59fe804e 100644 --- a/Gems/FastNoise/gem.json +++ b/Gems/FastNoise/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The FastNoise Gradient Gem uses the third-party, open source FastNoise library to provide a variety of high-performance noise generation algorithms.", - "canonical_tags": ["Gem"], - "user_tags": ["Utility", "Tools", "Design"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Utility", + "Tools", + "Design" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/fast-noise/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/fast-noise/", + "dependencies": [ + "GradientSignal", + "LmbrCentral", + "SurfaceData" + ] } diff --git a/Gems/GameState/gem.json b/Gems/GameState/gem.json index a1f272c290..7bb3cf4214 100644 --- a/Gems/GameState/gem.json +++ b/Gems/GameState/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Game State Gem provides a generic framework to determine and manage game states and game state transitions in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Framework", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Framework", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/game-state/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/game-state/", + "dependencies": [] } diff --git a/Gems/GameStateSamples/gem.json b/Gems/GameStateSamples/gem.json index f0dd82c5fa..be982b9c5b 100644 --- a/Gems/GameStateSamples/gem.json +++ b/Gems/GameStateSamples/gem.json @@ -5,9 +5,25 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Game State Samples Gem provides a set of sample game states (built on top of the Game State Gem), including primary user selection, main menu, level loading, level running, and level paused.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Sample", "Assets"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Sample", + "Assets" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/game-state-samples/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/game-state-samples/", + "dependencies": [ + "GameState", + "LocalUser", + "LyShine", + "SaveData", + "MessagePopup", + "LmbrCentral", + "UiBasics", + "LyShineExamples" + ] } diff --git a/Gems/Gestures/gem.json b/Gems/Gestures/gem.json index 4412e58c6d..fcc56b4704 100644 --- a/Gems/Gestures/gem.json +++ b/Gems/Gestures/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Gestures Gem provides detection for common gesture-based input actions on iOS and Android devices.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/gestures/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/gestures/", + "dependencies": [ + "Atom_RPI" + ] } diff --git a/Gems/GradientSignal/gem.json b/Gems/GradientSignal/gem.json index 67afb1f819..e87ccfe13a 100644 --- a/Gems/GradientSignal/gem.json +++ b/Gems/GradientSignal/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Gradient Signal Gem provides a number of components for generating, modifying, and mixing gradient signals.", - "canonical_tags": ["Gem"], - "user_tags": ["Utility", "Tools", "Design"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Utility", + "Tools", + "Design" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/gradient-signal/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/gradient-signal/", + "dependencies": [ + "SurfaceData", + "ImageProcessingAtom", + "LmbrCentral" + ] } diff --git a/Gems/GraphCanvas/gem.json b/Gems/GraphCanvas/gem.json index ab1e7ac77a..760bd157df 100644 --- a/Gems/GraphCanvas/gem.json +++ b/Gems/GraphCanvas/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Graph Canvas Gem provides a C++ framework for creating custom graphical node based editors for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Framework", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Framework", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/framework/graph-canvas/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/framework/graph-canvas/", + "dependencies": [] } diff --git a/Gems/GraphModel/gem.json b/Gems/GraphModel/gem.json index 62f1effb7c..256de75f6c 100644 --- a/Gems/GraphModel/gem.json +++ b/Gems/GraphModel/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Graph Model Gem provides a generic node graph data model framework for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Framework", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Framework", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/framework/graph-model/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/framework/graph-model/", + "dependencies": [ + "GraphCanvas" + ] } diff --git a/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp b/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp index 523d84f0f5..d33f37a2d7 100644 --- a/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp +++ b/Gems/HttpRequestor/Code/Source/HttpRequestManager.cpp @@ -49,7 +49,6 @@ namespace HttpRequestor { m_thread.join(); } - } void Manager::AddRequest(Parameters && httpRequestParameters) diff --git a/Gems/HttpRequestor/gem.json b/Gems/HttpRequestor/gem.json index e5eb8d6f44..eb1a112b0e 100644 --- a/Gems/HttpRequestor/gem.json +++ b/Gems/HttpRequestor/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The HTTP Requestor Gem provides functionality to make asynchronous HTTP/HTTPS requests and return data through a user-provided call back function.", - "canonical_tags": ["Gem"], - "user_tags": ["Network", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Network", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/http-requestor/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/http-requestor/", + "dependencies": [] } diff --git a/Gems/ImGui/gem.json b/Gems/ImGui/gem.json index deaac9925a..c1d89d1728 100644 --- a/Gems/ImGui/gem.json +++ b/Gems/ImGui/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Immediate Mode GUI Gem provides the 3rdParty library IMGUI which can be used to create run time immediate mode overlays for debugging and profiling information in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Rendering", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Rendering", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/imgui/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/debug/imgui/", + "dependencies": [ + "LmbrCentral" + ] } diff --git a/Gems/InAppPurchases/gem.json b/Gems/InAppPurchases/gem.json index ab43075896..1f1debd5fb 100644 --- a/Gems/InAppPurchases/gem.json +++ b/Gems/InAppPurchases/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The In-App Purchases Gem provides functionality for in app purchases for iOS and Android.", - "canonical_tags": ["Gem"], - "user_tags": ["SDK", "Network"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "SDK", + "Network" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/sdk/in-app-purchases/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/sdk/in-app-purchases/", + "dependencies": [] } diff --git a/Gems/LandscapeCanvas/Code/Source/Editor/MainWindow.cpp b/Gems/LandscapeCanvas/Code/Source/Editor/MainWindow.cpp index 836eec682e..4574b938f6 100644 --- a/Gems/LandscapeCanvas/Code/Source/Editor/MainWindow.cpp +++ b/Gems/LandscapeCanvas/Code/Source/Editor/MainWindow.cpp @@ -2512,6 +2512,26 @@ namespace LandscapeCanvasEditor { // See comment above in OnPrefabInstancePropagationBegin m_prefabPropagationInProgress = false; + + // After prefab propagation is complete, the entity tied to one of our open + // graphs might have been deleted (e.g. if a prefab was created from that entity). + // Any open graphs tied to an entity that no longer exists will need to be closed. + // We need to close them in a separate iterator because the CloseEditor API will + // end up modifying m_dockWidgetsByEntity. + AZStd::vector dockWidgetsToDelete; + for (auto [entityId, dockWidgetId] : m_dockWidgetsByEntity) + { + AZ::Entity* entity = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, entityId); + if (!entity) + { + dockWidgetsToDelete.push_back(dockWidgetId); + } + } + for (auto dockWidgetId : dockWidgetsToDelete) + { + CloseEditor(dockWidgetId); + } } void MainWindow::OnCryEditorEndCreate() diff --git a/Gems/LandscapeCanvas/gem.json b/Gems/LandscapeCanvas/gem.json index 63a80ec57e..ce0c64b75d 100644 --- a/Gems/LandscapeCanvas/gem.json +++ b/Gems/LandscapeCanvas/gem.json @@ -5,9 +5,23 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Landscape Canvas Gem provides the Landscape Canvas editor, a node-based graph tool for authoring workflows to populate landscape with dynamic vegetation.", - "canonical_tags": ["Gem"], - "user_tags": ["Environment", "Design", "Tools"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Environment", + "Design", + "Tools" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/landscape-canvas/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/landscape-canvas/", + "dependencies": [ + "GraphModel", + "GradientSignal", + "SurfaceData", + "Vegetation", + "LmbrCentral", + "GraphCanvas" + ] } diff --git a/Gems/LmbrCentral/gem.json b/Gems/LmbrCentral/gem.json index b6442bfd06..36de4c4ed3 100644 --- a/Gems/LmbrCentral/gem.json +++ b/Gems/LmbrCentral/gem.json @@ -5,10 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The O3DE Core (LmbrCentral) Gem provides required code and assets for running Open 3D Engine Editor.", - "canonical_tags": ["Gem"], - "user_tags": ["Core", "Framework", "Assets"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Core", + "Framework", + "Assets" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/core/lmbr-central/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/core/lmbr-central/", + "dependencies": [] } - diff --git a/Gems/LocalUser/gem.json b/Gems/LocalUser/gem.json index dcf4d44abb..f86e6e1bf6 100644 --- a/Gems/LocalUser/gem.json +++ b/Gems/LocalUser/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Local User Gem provides functionality for mapping local user ids to local player slots and managing local user profiles.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/local-user/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/local-user/", + "dependencies": [] } diff --git a/Gems/LyShine/gem.json b/Gems/LyShine/gem.json index e0fdb860e5..e2da8afa4d 100644 --- a/Gems/LyShine/gem.json +++ b/Gems/LyShine/gem.json @@ -5,9 +5,24 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The LyShine Gem provides the runtime UI system and creation tools for Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["UI", "Tools", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "UI", + "Tools", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/lyshine/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/lyshine/", + "dependencies": [ + "LmbrCentral", + "Atom_RPI", + "Atom", + "Atom_Bootstrap", + "AtomFont", + "TextureAtlas", + "AtomToolsFramework" + ] } diff --git a/Gems/LyShineExamples/gem.json b/Gems/LyShineExamples/gem.json index a411bfb363..122273f6d9 100644 --- a/Gems/LyShineExamples/gem.json +++ b/Gems/LyShineExamples/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The LyShine Examples Gem provides example code and assets for LyShine, the runtime UI system and editor for Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["UI", "Sample", "Assets"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "UI", + "Sample", + "Assets" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/lyshine-examples/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/lyshine-examples/", + "dependencies": [ + "LmbrCentral", + "LyShine" + ] } diff --git a/Gems/Maestro/gem.json b/Gems/Maestro/gem.json index 6ee989aa55..5149df7c14 100644 --- a/Gems/Maestro/gem.json +++ b/Gems/Maestro/gem.json @@ -5,10 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Maestro Cinematics Gem provides Track View, Open 3D Engine's animated sequence and cinematics editor.", - "canonical_tags": ["Gem"], - "user_tags": ["Animation", "Tools", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Animation", + "Tools", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/animation/maestro/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/animation/maestro/", + "dependencies": [ + "LmbrCentral" + ] } - diff --git a/Gems/MessagePopup/gem.json b/Gems/MessagePopup/gem.json index 2560b8869a..05d36bc2df 100644 --- a/Gems/MessagePopup/gem.json +++ b/Gems/MessagePopup/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Message Popup Gem provides an example implementation of popup messages using LyShine in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Sample"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Sample" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/message-popup/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/message-popup/", + "dependencies": [] } diff --git a/Gems/Metastream/gem.json b/Gems/Metastream/gem.json index 429d174cf5..862b17dd1d 100644 --- a/Gems/Metastream/gem.json +++ b/Gems/Metastream/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Metastream Gem provides functionality for an HTTP server that allows broadcasters to customize game streams with overlays of statistics and event data from a game session.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Network", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Network", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/twitch/metastream/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/twitch/metastream/", + "dependencies": [] } diff --git a/Gems/Microphone/gem.json b/Gems/Microphone/gem.json index cc47cbcd7e..68492ea786 100644 --- a/Gems/Microphone/gem.json +++ b/Gems/Microphone/gem.json @@ -5,9 +5,17 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Microphone Gem provides support for audio input through microphones.", - "canonical_tags": ["Gem"], - "user_tags": ["Audio", "Input"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Audio", + "Input" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/microphone/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/audio/microphone/", + "dependencies": [ + "AudioSystem" + ] } diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h index d09de1ad0d..743315e8bc 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h @@ -199,6 +199,8 @@ namespace Multiplayer friend class NetworkEntityManager; friend class EntityReplicationManager; + + friend class HierarchyTests; }; bool NetworkRoleHasController(NetEntityRole networkRole); diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h new file mode 100644 index 0000000000..45119a89e3 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyBus.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +namespace Multiplayer +{ + using NetworkHierarchyChangedEvent = AZ::Event; + using NetworkHierarchyLeaveEvent = AZ::Event<>; + + class NetworkHierarchyRequests + : public AZ::ComponentBus + { + public: + //! @returns true if the entity a hierarchical component attached should be considered for inclusion in a hierarchy + //! this should return false when an entity is deactivating + virtual bool IsHierarchyEnabled() const = 0; + + //! @returns hierarchical entities, the first element is the top level root + virtual AZStd::vector GetHierarchicalEntities() const = 0; + + //! @returns the top level root of a hierarchy, or nullptr if this entity is not in a hierarchy + virtual AZ::Entity* GetHierarchicalRoot() const = 0; + + //! @return true if this entity is a child entity within a hierarchy + virtual bool IsHierarchicalChild() const = 0; + + //! @return true if this entity is the top level root of a hierarchy + virtual bool IsHierarchicalRoot() const = 0; + + //! Binds the provided NetworkHierarchyChangedEvent handler to a Network Hierarchy component. + //! @param handler the handler to invoke when the entity's network hierarchy has been modified. + virtual void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) = 0; + + //! Binds the provided NetworkHierarchyLeaveEvent handler to a Network Hierarchy component. + //! @param handler the handler to invoke when the entity left its network hierarchy. + virtual void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) = 0; + }; + + typedef AZ::EBus NetworkHierarchyRequestBus; +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h new file mode 100644 index 0000000000..544fb3d6cb --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyChildComponent.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace Multiplayer +{ + class NetworkHierarchyRootComponent; + + //! @class NetworkHierarchyChildComponent + //! @brief Component that declares network dependency on the parent of this entity + /* + * The parent of this entity should have @NetworkHierarchyChildComponent (or @NetworkHierarchyRootComponent). + * A network hierarchy is a collection of entities with one @NetworkHierarchyRootComponent at the top parent + * and one or more @NetworkHierarchyChildComponent on its child entities. + */ + class NetworkHierarchyChildComponent final + : public NetworkHierarchyChildComponentBase + , public NetworkHierarchyRequestBus::Handler + { + friend class NetworkHierarchyRootComponent; + + public: + AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyChildComponent, s_networkHierarchyChildComponentConcreteUuid, Multiplayer::NetworkHierarchyChildComponentBase); + + static void Reflect(AZ::ReflectContext* context); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + + NetworkHierarchyChildComponent(); + + //! NetworkHierarchyChildComponentBase overrides. + //! @{ + void OnInit() override; + void OnActivate(EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(EntityIsMigrating entityIsMigrating) override; + //! @} + + //! NetworkHierarchyRequestBus overrides. + //! @{ + bool IsHierarchyEnabled() const override; + bool IsHierarchicalChild() const override; + bool IsHierarchicalRoot() const override { return false; } + AZ::Entity* GetHierarchicalRoot() const override; + AZStd::vector GetHierarchicalEntities() const override; + void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) override; + void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override; + //! @} + + protected: + //! Used by @NetworkHierarchyRootComponent + void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); + + private: + AZ::ChildChangedEvent::Handler m_childChangedHandler; + AZ::ParentChangedEvent::Handler m_parentChangedHandler; + + void OnChildChanged(AZ::ChildChangeType type, AZ::EntityId child); + void OnParentChanged(AZ::EntityId oldParent, AZ::EntityId parent); + + //! Points to the top level root. + AZ::Entity* m_rootEntity = nullptr; + + AZ::Event::Handler m_hierarchyRootNetIdChanged; + void OnHierarchyRootNetIdChanged(NetEntityId rootNetId); + + NetworkHierarchyChangedEvent m_networkHierarchyChangedEvent; + NetworkHierarchyLeaveEvent m_networkHierarchyLeaveEvent; + + //! Set to false when deactivating or otherwise not to be included in hierarchy considerations. + bool m_isHierarchyEnabled = true; + + void NotifyChildrenHierarchyDisbanded(); + }; +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h new file mode 100644 index 0000000000..4c9f94c004 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace Multiplayer +{ + //! @class NetworkHierarchyRootComponent + //! @brief Component that declares the top level entity of a network hierarchy. + /* + * Call @GetHierarchicalEntities to get the list of hierarchical entities. + * A network hierarchy is meant to be a small group of entities. You can control the maximum supported size of + * a network hierarchy by modifying CVar @bg_hierarchyEntityMaxLimit. + * + * A root component marks either a top most root of a hierarchy, or an inner root of an attach hierarchy. + */ + class NetworkHierarchyRootComponent final + : public NetworkHierarchyRootComponentBase + , public NetworkHierarchyRequestBus::Handler + { + friend class NetworkHierarchyChildComponent; + public: + AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyRootComponent, s_networkHierarchyRootComponentConcreteUuid, Multiplayer::NetworkHierarchyRootComponentBase); + + static void Reflect(AZ::ReflectContext* context); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + + NetworkHierarchyRootComponent(); + + //! NetworkHierarchyRootComponentBase overrides. + //! @{ + void OnInit() override; + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + //! @} + + //! NetworkHierarchyRequestBus overrides. + //! @{ + bool IsHierarchyEnabled() const override; + bool IsHierarchicalRoot() const override; + bool IsHierarchicalChild() const override; + AZStd::vector GetHierarchicalEntities() const override; + AZ::Entity* GetHierarchicalRoot() const override; + void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) override; + void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override; + //! @} + + protected: + void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); + + private: + AZ::ChildChangedEvent::Handler m_childChangedHandler; + AZ::ParentChangedEvent::Handler m_parentChangedHandler; + + void OnChildChanged(AZ::ChildChangeType type, AZ::EntityId child); + void OnParentChanged(AZ::EntityId oldParent, AZ::EntityId parent); + + NetworkHierarchyChangedEvent m_networkHierarchyChangedEvent; + NetworkHierarchyLeaveEvent m_networkHierarchyLeaveEvent; + + //! Points to the top level root, if this root is an inner root in this hierarchy. + AZ::Entity* m_rootEntity = nullptr; + + AZStd::vector m_hierarchicalEntities; + + //! Rebuilds hierarchy starting from this root component's entity. + void RebuildHierarchy(); + + //! @param underEntity Walk the child entities that belong to @underEntity and consider adding them to the hierarchy + //! @param currentEntityCount The total number of entities in the hierarchy prior to calling this method, + //! used to avoid adding too many entities to the hierarchy while walking recursively the relevant entities. + //! @currentEntityCount will be modified to reflect the total entity count upon completion of this method. + //! @returns false if an attempt was made to go beyond the maximum supported hierarchy size, true otherwise + bool RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount); + + //! @param entity Add the child entity and any of its relevant children to the hierarchy + //! @param currentEntityCount The total number of entities in the hierarchy prior to calling this method, + //! used to avoid adding too many entities to the hierarchy while walking recursively the relevant entities. + //! @currentEntityCount will be modified to reflect the total entity count upon completion of this method. + //! @returns false if an attempt was made to go beyond the maximum supported hierarchy size, true otherwise + bool RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount); + + void SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity); + + //! Set to false when deactivating or otherwise not to be included in hierarchy considerations. + bool m_isHierarchyEnabled = true; + }; +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h index a018279a79..35bf6e9f50 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h @@ -31,9 +31,11 @@ namespace Multiplayer private: void OnPreRender(float deltaTime); void OnCorrection(); - + void OnParentChanged(NetEntityId parentId); + EntityPreRenderEvent::Handler m_entityPreRenderEventHandler; EntityCorrectionEvent::Handler m_entityCorrectionEventHandler; + AZ::Event::Handler m_parentChangedEventHandler; Multiplayer::HostFrameId m_targetHostFrameId = HostFrameId(0); }; @@ -49,7 +51,9 @@ namespace Multiplayer private: void OnTransformChangedEvent(const AZ::Transform& worldTm); + void OnParentIdChangedEvent(AZ::EntityId oldParent, AZ::EntityId newParent); AZ::TransformChangedEvent::Handler m_transformChangedHandler; + AZ::ParentChangedEvent::Handler m_parentIdChangedHandler; }; } diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h index 8a5fec869c..5bcf038eff 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace Multiplayer { @@ -75,6 +76,15 @@ namespace Multiplayer const AZ::Transform& transform ) = 0; + //! Requests a network spawnable to instantiate at a given transform + //! This is an async function. The instantiated entities are not available immediately but will be constructed by the spawnable system + //! The spawnable ticket has to be kept for the whole lifetime of the entities + //! @param netSpawnable the network spawnable to spawn + //! @param transform the transform where the spawnable should be spawned + //! @return the ticket for managing the spawned entities + [[nodiscard]] virtual AZStd::unique_ptr RequestNetSpawnableInstantiation( + const AZ::Data::Asset& netSpawnable, const AZ::Transform& transform) = 0; + //! Configures new networked entity //! @param netEntity the entity to setup //! @param prefabEntryId the name of the spawnable the entity originated from diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.xml new file mode 100644 index 0000000000..46523d3724 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml new file mode 100644 index 0000000000..0f33e1f642 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp new file mode 100644 index 0000000000..3124eccaa8 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyChildComponent.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Multiplayer +{ + void NetworkHierarchyChildComponent::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Network Hierarchy Child", "Declares a network dependency on the root of this hierarchy.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Multiplayer") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) + ; + } + } + NetworkHierarchyChildComponentBase::Reflect(context); + } + + void NetworkHierarchyChildComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("NetworkTransformComponent")); + } + + void NetworkHierarchyChildComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent")); + } + + void NetworkHierarchyChildComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent")); + incompatible.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent")); + } + + NetworkHierarchyChildComponent::NetworkHierarchyChildComponent() + : m_childChangedHandler([this](AZ::ChildChangeType type, AZ::EntityId child) { OnChildChanged(type, child); }) + , m_parentChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId parent) { OnParentChanged(oldParent, parent); }) + , m_hierarchyRootNetIdChanged([this](NetEntityId rootNetId) {OnHierarchyRootNetIdChanged(rootNetId); }) + { + + } + + void NetworkHierarchyChildComponent::OnInit() + { + } + + void NetworkHierarchyChildComponent::OnActivate([[maybe_unused]] EntityIsMigrating entityIsMigrating) + { + m_isHierarchyEnabled = true; + + HierarchyRootAddEvent(m_hierarchyRootNetIdChanged); + NetworkHierarchyRequestBus::Handler::BusConnect(GetEntityId()); + + if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent()) + { + transformComponent->BindChildChangedEventHandler(m_childChangedHandler); + transformComponent->BindParentChangedEventHandler(m_parentChangedHandler); + } + } + + void NetworkHierarchyChildComponent::OnDeactivate([[maybe_unused]] EntityIsMigrating entityIsMigrating) + { + m_isHierarchyEnabled = false; + + if (m_rootEntity) + { + if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent()) + { + root->RebuildHierarchy(); + } + } + + NotifyChildrenHierarchyDisbanded(); + + NetworkHierarchyRequestBus::Handler::BusDisconnect(); + } + + bool NetworkHierarchyChildComponent::IsHierarchyEnabled() const + { + return m_isHierarchyEnabled; + } + + bool NetworkHierarchyChildComponent::IsHierarchicalChild() const + { + return GetHierarchyRoot() != InvalidNetEntityId; + } + + AZ::Entity* NetworkHierarchyChildComponent::GetHierarchicalRoot() const + { + return m_rootEntity; + } + + AZStd::vector NetworkHierarchyChildComponent::GetHierarchicalEntities() const + { + if (m_rootEntity) + { + return m_rootEntity->FindComponent()->GetHierarchicalEntities(); + } + + return {}; + } + + void NetworkHierarchyChildComponent::BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) + { + handler.Connect(m_networkHierarchyChangedEvent); + } + + void NetworkHierarchyChildComponent::BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) + { + handler.Connect(m_networkHierarchyLeaveEvent); + } + + void NetworkHierarchyChildComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot) + { + m_rootEntity = hierarchyRoot; + if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) + { + NetworkHierarchyChildComponentController* controller = static_cast(GetController()); + if (m_rootEntity) + { + const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId()); + controller->SetHierarchyRoot(netRootId); + + m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId()); + } + else + { + controller->SetHierarchyRoot(InvalidNetEntityId); + + m_networkHierarchyLeaveEvent.Signal(); + } + } + + if (m_rootEntity == nullptr) + { + NotifyChildrenHierarchyDisbanded(); + } + } + + void NetworkHierarchyChildComponent::OnChildChanged([[maybe_unused]] AZ::ChildChangeType type, [[maybe_unused]] AZ::EntityId child) + { + if (m_rootEntity) + { + if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent()) + { + root->RebuildHierarchy(); + } + } + } + + void NetworkHierarchyChildComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, [[maybe_unused]] AZ::EntityId parent) + { + if (m_rootEntity) + { + if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent()) + { + root->RebuildHierarchy(); + } + } + } + + void NetworkHierarchyChildComponent::OnHierarchyRootNetIdChanged(NetEntityId rootNetId) + { + ConstNetworkEntityHandle rootHandle = GetNetworkEntityManager()->GetEntity(rootNetId); + if (rootHandle.Exists()) + { + AZ::Entity* newRoot = rootHandle.GetEntity(); + if (m_rootEntity != newRoot) + { + m_rootEntity = newRoot; + m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId()); + } + } + else + { + m_isHierarchyEnabled = false; + m_rootEntity = nullptr; + m_networkHierarchyLeaveEvent.Signal(); + } + } + + void NetworkHierarchyChildComponent::NotifyChildrenHierarchyDisbanded() + { + AZStd::vector allChildren; + AZ::TransformBus::EventResult(allChildren, GetEntityId(), &AZ::TransformBus::Events::GetChildren); + for (const AZ::EntityId& childEntityId : allChildren) + { + if (const AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(childEntityId)) + { + if (auto* hierarchyChildComponent = childEntity->FindComponent()) + { + hierarchyChildComponent->SetTopLevelHierarchyRootEntity(nullptr); + } + else if (auto* hierarchyRootComponent = childEntity->FindComponent()) + { + hierarchyRootComponent->SetTopLevelHierarchyRootEntity(nullptr); + } + } + } + } +} diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp new file mode 100644 index 0000000000..4a5e9ce8d2 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -0,0 +1,329 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +AZ_CVAR(uint32_t, bg_hierarchyEntityMaxLimit, 16, nullptr, AZ::ConsoleFunctorFlags::Null, + "Maximum allowed size of network entity hierarchies, including top level entity."); + +namespace Multiplayer +{ + void NetworkHierarchyRootComponent::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class( + "Network Hierarchy Root", "Marks the entity as the root of an entity hierarchy.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Multiplayer") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) + ; + } + } + NetworkHierarchyRootComponentBase::Reflect(context); + } + + void NetworkHierarchyRootComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.push_back(AZ_CRC_CE("NetworkTransformComponent")); + } + + void NetworkHierarchyRootComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent")); + } + + void NetworkHierarchyRootComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent")); + incompatible.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent")); + } + + NetworkHierarchyRootComponent::NetworkHierarchyRootComponent() + : m_childChangedHandler([this](AZ::ChildChangeType type, AZ::EntityId child) { OnChildChanged(type, child); }) + , m_parentChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId parent) { OnParentChanged(oldParent, parent); }) + { + } + + void NetworkHierarchyRootComponent::OnInit() + { + } + + void NetworkHierarchyRootComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + m_isHierarchyEnabled = true; + m_hierarchicalEntities.push_back(GetEntity()); + + NetworkHierarchyRequestBus::Handler::BusConnect(GetEntityId()); + + if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent()) + { + transformComponent->BindChildChangedEventHandler(m_childChangedHandler); + transformComponent->BindParentChangedEventHandler(m_parentChangedHandler); + } + } + + void NetworkHierarchyRootComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + m_isHierarchyEnabled = false; + + if (m_rootEntity) + { + // Tell parent to re-build the hierarchy + if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent()) + { + root->RebuildHierarchy(); + } + } + else + { + // Notify children that the hierarchy is disbanding + AZStd::vector allChildren; + AZ::TransformBus::EventResult(allChildren, GetEntityId(), &AZ::TransformBus::Events::GetChildren); + + for (const AZ::EntityId& childEntityId : allChildren) + { + if (const AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(childEntityId)) + { + SetRootForEntity(nullptr, childEntity); + } + } + } + + m_childChangedHandler.Disconnect(); + m_parentChangedHandler.Disconnect(); + + NetworkHierarchyRequestBus::Handler::BusDisconnect(); + + m_hierarchicalEntities.clear(); + m_rootEntity = nullptr; + } + + bool NetworkHierarchyRootComponent::IsHierarchyEnabled() const + { + return m_isHierarchyEnabled; + } + + bool NetworkHierarchyRootComponent::IsHierarchicalRoot() const + { + return GetHierarchyRoot() == InvalidNetEntityId; + } + + bool NetworkHierarchyRootComponent::IsHierarchicalChild() const + { + return !IsHierarchicalRoot(); + } + + AZStd::vector NetworkHierarchyRootComponent::GetHierarchicalEntities() const + { + return m_hierarchicalEntities; + } + + AZ::Entity* NetworkHierarchyRootComponent::GetHierarchicalRoot() const + { + if (m_rootEntity) + { + return m_rootEntity; + } + + return GetEntity(); + } + + void NetworkHierarchyRootComponent::BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) + { + handler.Connect(m_networkHierarchyChangedEvent); + } + + void NetworkHierarchyRootComponent::BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) + { + handler.Connect(m_networkHierarchyLeaveEvent); + } + + void NetworkHierarchyRootComponent::OnChildChanged([[maybe_unused]] AZ::ChildChangeType type, [[maybe_unused]] AZ::EntityId child) + { + if (IsHierarchicalRoot()) + { + // Parent-child notifications are not reliable enough to avoid duplicate notifications, + // so we will rebuild from scratch to avoid duplicate entries in @m_hierarchicalEntities. + RebuildHierarchy(); + } + else if (NetworkHierarchyRootComponent* root = GetHierarchicalRoot()->FindComponent()) + { + root->RebuildHierarchy(); + } + } + + void NetworkHierarchyRootComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent) + { + // If the parent is part of a hierarchy, it will detect this entity as a new child and rebuild hierarchy. + // Thus, we only need to take care of a case when the parent is not part of a hierarchy, + // in which case, this entity will be a new root of a new hierarchy. + + if (AZ::Entity* parentEntity = AZ::Interface::Get()->FindEntity(newParent)) + { + if (parentEntity->FindComponent() == nullptr && + parentEntity->FindComponent() == nullptr) + { + RebuildHierarchy(); + } + else + { + m_hierarchicalEntities.clear(); + } + } + else + { + // Detached from parent + RebuildHierarchy(); + } + } + + void NetworkHierarchyRootComponent::RebuildHierarchy() + { + AZStd::vector previousEntities; + m_hierarchicalEntities.swap(previousEntities); + + m_hierarchicalEntities.push_back(GetEntity()); // Add the root. + + uint32_t currentEntityCount = aznumeric_cast(m_hierarchicalEntities.size()); + RecursiveAttachHierarchicalEntities(GetEntityId(), currentEntityCount); + + bool hierarchyChanged = false; + + // Send out join and leave events. + for (AZ::Entity* currentEntity : m_hierarchicalEntities) + { + const auto prevEntityIterator = AZStd::find(previousEntities.begin(), previousEntities.end(), currentEntity); + if (prevEntityIterator != previousEntities.end()) + { + // This entity was here before the build of the hierarchy. + previousEntities.erase(prevEntityIterator); + } + else + { + // This is a newly added entity to the network hierarchy. + hierarchyChanged = true; + SetRootForEntity(GetEntity(), currentEntity); + } + } + + // These entities were removed since last rebuild. + for (const AZ::Entity* previousEntity : previousEntities) + { + SetRootForEntity(nullptr, previousEntity); + } + + if (!previousEntities.empty()) + { + hierarchyChanged = true; + } + + if (hierarchyChanged) + { + m_networkHierarchyChangedEvent.Signal(GetEntityId()); + } + } + + void NetworkHierarchyRootComponent::SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity) + { + if (auto* hierarchyChildComponent = childEntity->FindComponent()) + { + hierarchyChildComponent->SetTopLevelHierarchyRootEntity(root); + } + else if (auto* hierarchyRootComponent = childEntity->FindComponent()) + { + hierarchyRootComponent->SetTopLevelHierarchyRootEntity(root); + } + } + + bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount) + { + AZStd::vector allChildren; + AZ::TransformBus::EventResult(allChildren, underEntity, &AZ::TransformBus::Events::GetChildren); + + for (const AZ::EntityId& newChildId : allChildren) + { + if (!RecursiveAttachHierarchicalChild(newChildId, currentEntityCount)) + { + return false; + } + } + + return true; + } + + bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount) + { + if (currentEntityCount >= bg_hierarchyEntityMaxLimit) + { + AZLOG_WARN("Entity %s is trying to build a network hierarchy that is too large. bg_hierarchyEntityMaxLimit is currently set to (%u)", + GetEntity()->GetName().c_str(), static_cast(bg_hierarchyEntityMaxLimit)); + return false; + } + + if (AZ::Entity* childEntity = AZ::Interface::Get()->FindEntity(entity)) + { + auto* hierarchyChildComponent = childEntity->FindComponent(); + auto* hierarchyRootComponent = childEntity->FindComponent(); + + if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchyEnabled()) || + (hierarchyRootComponent && hierarchyRootComponent->IsHierarchyEnabled())) + { + m_hierarchicalEntities.push_back(childEntity); + ++currentEntityCount; + + if (!RecursiveAttachHierarchicalEntities(entity, currentEntityCount)) + { + return false; + } + } + } + + return true; + } + + void NetworkHierarchyRootComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot) + { + m_rootEntity = hierarchyRoot; + + if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority) + { + NetworkHierarchyChildComponentController* controller = static_cast(GetController()); + if (hierarchyRoot) + { + const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(hierarchyRoot->GetId()); + controller->SetHierarchyRoot(netRootId); + } + else + { + controller->SetHierarchyRoot(InvalidNetEntityId); + } + } + + if (m_rootEntity == nullptr) + { + // We lost the parent hierarchical entity, so as a root we need to re-build our own hierarchy. + RebuildHierarchy(); + } + } +} diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp index 1d9fee16e0..d284f100ff 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp @@ -28,6 +28,7 @@ namespace Multiplayer NetworkTransformComponent::NetworkTransformComponent() : m_entityPreRenderEventHandler([this](float deltaTime) { OnPreRender(deltaTime); }) , m_entityCorrectionEventHandler([this]() { OnCorrection(); }) + , m_parentChangedEventHandler([this](NetEntityId parentId) { OnParentChanged(parentId); }) { ; } @@ -41,6 +42,7 @@ namespace Multiplayer { GetNetBindComponent()->AddEntityPreRenderEventHandler(m_entityPreRenderEventHandler); GetNetBindComponent()->AddEntityCorrectionEventHandler(m_entityCorrectionEventHandler); + ParentEntityIdAddEvent(m_parentChangedEventHandler); } void NetworkTransformComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) @@ -97,10 +99,26 @@ namespace Multiplayer } } + void NetworkTransformComponent::OnParentChanged(NetEntityId parentId) + { + const ConstNetworkEntityHandle parentEntityHandle = GetNetworkEntityManager()->GetEntity(parentId); + if (parentEntityHandle.Exists()) + { + if (const AZ::Entity* parentEntity = parentEntityHandle.GetEntity()) + { + GetEntity()->GetTransform()->SetParent(parentEntity->GetId()); + } + } + else + { + GetEntity()->GetTransform()->SetParent(AZ::EntityId()); + } + } NetworkTransformComponentController::NetworkTransformComponentController(NetworkTransformComponent& parent) : NetworkTransformComponentControllerBase(parent) , m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform& worldTm) { OnTransformChangedEvent(worldTm); }) + , m_parentIdChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId newParent) { OnParentIdChangedEvent(oldParent, newParent); }) { ; } @@ -109,6 +127,9 @@ namespace Multiplayer { GetParent().GetTransformComponent()->BindTransformChangedEventHandler(m_transformChangedHandler); OnTransformChangedEvent(GetParent().GetTransformComponent()->GetWorldTM()); + + GetParent().GetTransformComponent()->BindParentChangedEventHandler(m_parentIdChangedHandler); + OnParentIdChangedEvent(AZ::EntityId(), GetParent().GetTransformComponent()->GetParentId()); } void NetworkTransformComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) @@ -122,4 +143,14 @@ namespace Multiplayer SetTranslation(worldTm.GetTranslation()); SetScale(worldTm.GetUniformScale()); } + + void NetworkTransformComponentController::OnParentIdChangedEvent([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent) + { + AZ::Entity* parentEntity = AZ::Interface::Get()->FindEntity(newParent); + if (parentEntity) + { + const ConstNetworkEntityHandle parentHandle(parentEntity, GetNetworkEntityTracker()); + SetParentEntityId(parentHandle.GetNetEntityId()); + } + } } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp b/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp index 1ad7be1a0a..030964d81c 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp @@ -6,13 +6,14 @@ * */ +#include +#include +#include +#include #include #include #include -#include #include -#include -#include namespace Multiplayer { @@ -23,7 +24,6 @@ namespace Multiplayer AzNetworking::NetworkingSystemComponent::CreateDescriptor(), MultiplayerSystemComponent::CreateDescriptor(), NetBindComponent::CreateDescriptor(), - NetBindMarkerComponent::CreateDescriptor(), NetworkSpawnableHolderComponent::CreateDescriptor(), }); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index 482d3a1ee8..b45c05cda7 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -75,6 +75,8 @@ namespace Multiplayer void EntityReplicationManager::ActivatePendingEntities() { + AZStd::vector notReadyEntities; + const AZ::TimeMs endTimeMs = AZ::GetElapsedTimeMs() + m_entityActivationTimeSliceMs; while (!m_entitiesPendingActivation.empty()) { @@ -83,7 +85,14 @@ namespace Multiplayer EntityReplicator* entityReplicator = GetEntityReplicator(entityId); if (entityReplicator && !entityReplicator->IsMarkedForRemoval()) { - entityReplicator->ActivateNetworkEntity(); + if (entityReplicator->IsReadyToActivate()) + { + entityReplicator->ActivateNetworkEntity(); + } + else + { + notReadyEntities.push_back(entityId); + } } if (m_entityActivationTimeSliceMs > AZ::TimeMs{ 0 } && AZ::GetElapsedTimeMs() > endTimeMs) { @@ -91,6 +100,11 @@ namespace Multiplayer break; } } + + for (NetEntityId netEntityId : notReadyEntities) + { + m_entitiesPendingActivation.push_back(netEntityId); + } } void EntityReplicationManager::SendUpdates(AZ::TimeMs hostTimeMs) @@ -249,15 +263,15 @@ namespace Multiplayer void EntityReplicationManager::SendEntityUpdates(AZ::TimeMs hostTimeMs) { EntityReplicatorList toSendList = GenerateEntityUpdateList(); - + AZLOG(NET_ReplicationInfo, "Sending %zd updates from %d to %d", toSendList.size(), (uint8_t)GetNetworkEntityManager()->GetHostId(), (uint8_t)GetRemoteHostId()); - + // prep a replication record for send, at this point, everything needs to be sent for (EntityReplicator* replicator : toSendList) { replicator->GetPropertyPublisher()->PrepareSerialization(); } - + // While our to send list is not empty, build up another packet to send do { @@ -524,7 +538,7 @@ namespace Multiplayer bool EntityReplicationManager::HandlePropertyChangeMessage ( - AzNetworking::IConnection* invokingConnection, + AzNetworking::IConnection* invokingConnection, EntityReplicator* entityReplicator, AzNetworking::PacketId packetId, NetEntityId netEntityId, @@ -1137,7 +1151,7 @@ namespace Multiplayer AzNetworking::TrackChangedSerializer outputSerializer(message.m_propertyUpdateData.GetBuffer(), static_cast(message.m_propertyUpdateData.GetSize())); if (!HandlePropertyChangeMessage ( - invokingConnection, + invokingConnection, replicator, AzNetworking::InvalidPacketId, message.m_entityId, diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp index 14df1bb028..b684473ea3 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp @@ -6,23 +6,25 @@ * */ -#include -#include -#include -#include -#include -#include -#include #include #include +#include +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include -#include #include #include @@ -48,7 +50,7 @@ namespace Multiplayer , m_onForwardRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); }) , m_onSendAutonomousRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); }) , m_onForwardAutonomousRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); }) - , m_onEntityStopHandler([this](const ConstNetworkEntityHandle &) { OnEntityRemovedEvent(); }) + , m_onEntityStopHandler([this](const ConstNetworkEntityHandle&) { OnEntityRemovedEvent(); }) , m_proxyRemovalEvent([this] { OnProxyRemovalTimedEvent(); }, AZ::Name("ProxyRemovalTimedEvent")) { if (auto localEnt = m_entityHandle.GetEntity()) @@ -119,12 +121,12 @@ namespace Multiplayer { m_replicationManager.AddReplicatorToPendingSend(*this); m_propertyPublisher = AZStd::make_unique - ( - GetRemoteNetworkRole(), - !RemoteManagerOwnsEntityLifetime() ? PropertyPublisher::OwnsLifetime::True : PropertyPublisher::OwnsLifetime::False, - m_netBindComponent, - *m_connection - ); + ( + GetRemoteNetworkRole(), + !RemoteManagerOwnsEntityLifetime() ? PropertyPublisher::OwnsLifetime::True : PropertyPublisher::OwnsLifetime::False, + m_netBindComponent, + *m_connection + ); m_netBindComponent->AddEntityDirtiedEventHandler(m_onEntityDirtiedHandler); } else @@ -279,7 +281,7 @@ namespace Multiplayer AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent"); bool isAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) - && (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole()); + && (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole()); bool isClient = GetRemoteNetworkRole() == NetEntityRole::Client; bool isAutonomous = GetBoundLocalNetworkRole() == NetEntityRole::Autonomous; if (isAuthority || isClient || isAutonomous) @@ -306,9 +308,9 @@ namespace Multiplayer bool EntityReplicator::RemoteManagerOwnsEntityLifetime() const { bool isServer = (GetBoundLocalNetworkRole() == NetEntityRole::Server) - && (GetRemoteNetworkRole() == NetEntityRole::Authority); + && (GetRemoteNetworkRole() == NetEntityRole::Authority); bool isClient = (GetBoundLocalNetworkRole() == NetEntityRole::Client) - || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous); + || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous); return isServer || isClient; } @@ -405,6 +407,62 @@ namespace Multiplayer return m_replicationManager.GetResendTimeoutTimeMs(); } + bool EntityReplicator::IsReadyToActivate() const + { + const AZ::Entity* entity = m_entityHandle.GetEntity(); + AZ_Assert(entity, "Entity replicator entity unexpectedly missing"); + + const NetworkHierarchyChildComponent* hierarchyChildComponent = entity->FindComponent(); + const NetworkHierarchyRootComponent* hierarchyRootComponent = nullptr; + + if (hierarchyChildComponent == nullptr) + { + // Child and root hierarchy components are mutually exclusive + hierarchyRootComponent = entity->FindComponent(); + } + + if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchicalChild()) + || (hierarchyRootComponent && hierarchyRootComponent->IsHierarchicalChild())) + { + // If hierarchy is enabled for the entity, check if the parent is available + if (const NetworkTransformComponent* networkTransform = entity->FindComponent()) + { + const NetEntityId parentId = networkTransform->GetParentEntityId(); + /* + * For root entities attached to a level, a network parent won't be set. + * In this case, this entity is the root entity of the hierarchy and it will be activated first. + */ + if (parentId != InvalidNetEntityId) + { + ConstNetworkEntityHandle parentHandle = GetNetworkEntityManager()->GetEntity(parentId); + + const AZ::Entity* parentEntity = parentHandle.GetEntity(); + if (parentEntity && parentEntity->GetState() == AZ::Entity::State::Active) + { + AZLOG + ( + NET_HierarchyActivationInfo, + "Hierchical entity %s asking for activation - granted", + entity->GetName().c_str() + ); + return true; + } + + AZLOG + ( + NET_HierarchyActivationInfo, + "Hierchical entity %s asking for activation - waiting on the parent %u", + entity->GetName().c_str(), + aznumeric_cast(parentId) + ); + return false; + } + } + } + + return true; + } + NetworkEntityUpdateMessage EntityReplicator::GenerateUpdatePacket() { if (IsMarkedForRemoval() && OwnsReplicatorLifetime()) // TODO: clean this up diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.h b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.h index ec4bd8c4f5..e4dc62bc26 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.h @@ -36,7 +36,7 @@ namespace Multiplayer { public: EntityReplicator(EntityReplicationManager& replicationManager, AzNetworking::IConnection* connection, NetEntityRole remoteNetworkRole, const ConstNetworkEntityHandle& entityHandle); - virtual ~EntityReplicator(); + ~EntityReplicator() override; NetEntityRole GetBoundLocalNetworkRole() const; NetEntityRole GetRemoteNetworkRole() const; @@ -62,6 +62,8 @@ namespace Multiplayer bool IsDeletionAcknowledged() const; bool WasMigrated() const; void SetWasMigrated(bool wasMigrated); + // If an entity is part of a network hierarchy then it is only ready to activate when its direct parent entity is active. + bool IsReadyToActivate() const; NetworkEntityUpdateMessage GenerateUpdatePacket(); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index f981966ae1..bcd4c9aad8 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -465,8 +465,60 @@ namespace Multiplayer return netEntityId; } - void NetworkEntityManager::OnRootSpawnableAssigned( - [[maybe_unused]] AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) + AZStd::unique_ptr NetworkEntityManager::RequestNetSpawnableInstantiation( + const AZ::Data::Asset& netSpawnable, const AZ::Transform& transform) + { + // Prepare the parameters for the spawning process + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_priority = AzFramework::SpawnablePriority_High; + + const AZ::Name netSpawnableName = + AZ::Interface::Get()->GetSpawnableNameFromAssetId(netSpawnable.GetId()); + + if (netSpawnableName.IsEmpty()) + { + AZ_Error("NetworkEntityManager", false, + "RequestNetSpawnableInstantiation: Requested spawnable %s doesn't exist in the NetworkSpawnableLibrary. Please make sure it is a network spawnable", + netSpawnable.GetHint().c_str()); + return nullptr; + } + + // Pre-insertion callback allows us to do network-specific setup for the entities before they are added to the scene + optionalArgs.m_preInsertionCallback = [netSpawnableName, rootTransform = transform] + (AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities) + { + bool shouldUpdateTransform = (rootTransform.IsClose(AZ::Transform::Identity()) == false); + + for (uint32_t netEntityIndex = 0, entitiesSize = aznumeric_cast(entities.size()); + netEntityIndex < entitiesSize; ++netEntityIndex) + { + AZ::Entity* netEntity = *(entities.begin() + netEntityIndex); + + if (shouldUpdateTransform) + { + AzFramework::TransformComponent* netEntityTransform = + netEntity->FindComponent(); + + AZ::Transform worldTm = netEntityTransform->GetWorldTM(); + worldTm = rootTransform * worldTm; + netEntityTransform->SetWorldTM(worldTm); + } + + PrefabEntityId prefabEntityId; + prefabEntityId.m_prefabName = netSpawnableName; + prefabEntityId.m_entityOffset = netEntityIndex; + AZ::Interface::Get()->SetupNetEntity(netEntity, prefabEntityId, NetEntityRole::Authority); + } + }; + + // Spawn with the newly created ticket. This allows the calling code to manage the lifetime of the constructed entities + auto ticket = AZStd::make_unique(netSpawnable); + AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(*ticket, AZStd::move(optionalArgs)); + return ticket; + } + + void NetworkEntityManager::OnRootSpawnableAssigned(AZ::Data::Asset rootSpawnable, + [[maybe_unused]] uint32_t generation) { auto* multiplayer = GetMultiplayer(); const auto agentType = multiplayer->GetAgentType(); @@ -479,7 +531,6 @@ namespace Multiplayer void NetworkEntityManager::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation) { - // TODO: Do we need to clear all entities here? auto* multiplayer = GetMultiplayer(); const auto agentType = multiplayer->GetAgentType(); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h index 9ccc576447..50e6beedad 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h @@ -60,6 +60,9 @@ namespace Multiplayer const AZ::Transform& transform ) override; + AZStd::unique_ptr RequestNetSpawnableInstantiation( + const AZ::Data::Asset& netSpawnable, const AZ::Transform& transform) override; + void SetupNetEntity(AZ::Entity* netEntity, PrefabEntityId prefabEntityId, NetEntityRole netEntityRole) override; uint32_t GetEntityCount() const override; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityTracker.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityTracker.h index 09238acaee..0e632dc7b4 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityTracker.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityTracker.h @@ -37,6 +37,7 @@ namespace Multiplayer NetworkEntityHandle Get(NetEntityId netEntityId); ConstNetworkEntityHandle Get(NetEntityId netEntityId) const; + //! Returns Net Entity ID for a given AZ Entity ID. NetEntityId Get(const AZ::EntityId& entityId) const; //! Returns true if the netEntityId exists. diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp deleted file mode 100644 index c8abac25cb..0000000000 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Multiplayer -{ - void NetBindMarkerComponent::Reflect(AZ::ReflectContext* context) - { - AZ::SerializeContext* serializeContext = azrtti_cast(context); - if (serializeContext) - { - serializeContext->Class() - ->Version(1) - ->Field("NetEntityIndex", &NetBindMarkerComponent::m_netEntityIndex) - ->Field("NetSpawnableAsset", &NetBindMarkerComponent::m_networkSpawnableAsset); - } - } - - AzFramework::Spawnable* GetSpawnableFromAsset(AZ::Data::Asset& asset) - { - AzFramework::Spawnable* spawnable = asset.GetAs(); - if (!spawnable) - { - asset = - AZ::Data::AssetManager::Instance().GetAsset(asset.GetId(), AZ::Data::AssetLoadBehavior::PreLoad); - AZ::Data::AssetManager::Instance().BlockUntilLoadComplete(asset); - - spawnable = asset.GetAs(); - } - - return spawnable; - } - - - void NetBindMarkerComponent::Activate() - { - const auto agentType = AZ::Interface::Get()->GetAgentType(); - const bool spawnImmediately = - (agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer); - - if (spawnImmediately && m_networkSpawnableAsset.GetId().IsValid()) - { - AZ::Transform worldTm = GetEntity()->FindComponent()->GetWorldTM(); - auto preInsertionCallback = - [worldTm = AZStd::move(worldTm), netEntityIndex = m_netEntityIndex, spawnableAssetId = m_networkSpawnableAsset.GetId()] - (AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities) - { - if (entities.size() == 1) - { - AZ::Entity* netEntity = *entities.begin(); - - auto* transformComponent = netEntity->FindComponent(); - transformComponent->SetWorldTM(worldTm); - - AZ::Name spawnableName = AZ::Interface::Get()->GetSpawnableNameFromAssetId(spawnableAssetId); - PrefabEntityId prefabEntityId; - prefabEntityId.m_prefabName = spawnableName; - prefabEntityId.m_entityOffset = static_cast(netEntityIndex); - AZ::Interface::Get()->SetupNetEntity(netEntity, prefabEntityId, NetEntityRole::Authority); - } - else - { - AZ_Error("NetBindMarkerComponent", false, "Requested to spawn 1 entity, but received %d", entities.size()); - } - }; - - m_netSpawnTicket = AzFramework::EntitySpawnTicket(m_networkSpawnableAsset); - AzFramework::SpawnEntitiesOptionalArgs optionalArgs; - optionalArgs.m_preInsertionCallback = AZStd::move(preInsertionCallback); - AzFramework::SpawnableEntitiesInterface::Get()->SpawnEntities( - m_netSpawnTicket, { m_netEntityIndex }, AZStd::move(optionalArgs)); - } - } - - void NetBindMarkerComponent::Deactivate() - { - if(m_netSpawnTicket.IsValid()) - { - AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket); - } - } - - size_t NetBindMarkerComponent::GetNetEntityIndex() const - { - return m_netEntityIndex; - } - - void NetBindMarkerComponent::SetNetEntityIndex(size_t netEntityIndex) - { - m_netEntityIndex = netEntityIndex; - } - - void NetBindMarkerComponent::SetNetworkSpawnableAsset(AZ::Data::Asset networkSpawnableAsset) - { - m_networkSpawnableAsset = networkSpawnableAsset; - } - - AZ::Data::Asset NetBindMarkerComponent::GetNetworkSpawnableAsset() const - { - return m_networkSpawnableAsset; - } - -} diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.h b/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.h deleted file mode 100644 index dce3252200..0000000000 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetBindMarkerComponent.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include -#include -#include - -namespace Multiplayer -{ - //! @class NetBindMarkerComponent - //! @brief Component for tracking net entities in the original non-networked spawnable. - class NetBindMarkerComponent final : public AZ::Component - { - public: - AZ_COMPONENT(NetBindMarkerComponent, "{40612C1B-427D-45C6-A2F0-04E16DF5B718}"); - - static void Reflect(AZ::ReflectContext* context); - - NetBindMarkerComponent() = default; - ~NetBindMarkerComponent() override = default; - - //! AZ::Component overrides. - //! @{ - void Activate() override; - void Deactivate() override; - //! @} - - size_t GetNetEntityIndex() const; - void SetNetEntityIndex(size_t val); - - void SetNetworkSpawnableAsset(AZ::Data::Asset networkSpawnableAsset); - AZ::Data::Asset GetNetworkSpawnableAsset() const; - - private: - AZ::Data::Asset m_networkSpawnableAsset{AZ::Data::AssetLoadBehavior::PreLoad}; - size_t m_netEntityIndex = 0; - AzFramework::EntitySpawnTicket m_netSpawnTicket; - }; -} // namespace Multiplayer diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index acfec5eb38..e0990aa785 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include @@ -46,7 +45,7 @@ namespace Multiplayer { if (auto* serializeContext = azrtti_cast(context); serializeContext != nullptr) { - serializeContext->Class()->Version(1); + serializeContext->Class()->Version(2); } } @@ -137,8 +136,6 @@ namespace Multiplayer networkSpawnableAsset.Create(networkSpawnable->GetId()); networkSpawnableAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad); - size_t netEntitiesIndexCounter = 0; - for (auto* prefabEntity : prefabNetEntities) { Instance* instance = netEntityToInstanceMap[prefabEntity]; @@ -148,30 +145,11 @@ namespace Multiplayer AZ_Assert(netEntity, "Unable to detach entity %s [%s] from the source prefab instance", prefabEntity->GetName().c_str(), entityId.ToString().c_str()); - // Net entity will need a new ID to avoid IDs collision - netEntity->SetId(AZ::Entity::MakeId()); netEntity->InvalidateDependencies(); netEntity->EvaluateDependencies(); // Insert the entity into the target net spawnable netSpawnableEntities.emplace_back(netEntity); - - // Use the old ID for the breadcrumb entity to keep parent-child relationship in the original spawnable - AZ::Entity* breadcrumbEntity = aznew AZ::Entity(entityId, netEntity->GetName()); - breadcrumbEntity->SetRuntimeActiveByDefault(netEntity->IsRuntimeActiveByDefault()); - - // Marker component is responsible to spawning entities based on the index. - NetBindMarkerComponent* netBindMarkerComponent = breadcrumbEntity->CreateComponent(); - netBindMarkerComponent->SetNetEntityIndex(netEntitiesIndexCounter); - netBindMarkerComponent->SetNetworkSpawnableAsset(networkSpawnableAsset); - - // Copy the transform component from the original entity to have the correct transform and parent-child relationship - AzFramework::TransformComponent* transformComponent = netEntity->FindComponent(); - breadcrumbEntity->CreateComponent(*transformComponent); - - instance->AddEntity(*breadcrumbEntity); - - netEntitiesIndexCounter++; } // Add net spawnable asset holder to the prefab root diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp index c40cb3677b..d33c3274c2 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp @@ -8,6 +8,8 @@ #include #include +#include +#include namespace Multiplayer { @@ -28,10 +30,32 @@ namespace Multiplayer void NetworkSpawnableHolderComponent::Activate() { + const auto agentType = GetMultiplayer()->GetAgentType(); + const bool shouldSpawnNetEntities = + (agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer); + + if(shouldSpawnNetEntities) + { + AZ::Transform rootEntityTransform = AZ::Transform::CreateIdentity(); + + AzFramework::TransformComponent* rootEntityTransformComponent = + GetEntity()->FindComponent(); + if (rootEntityTransformComponent) + { + rootEntityTransform = rootEntityTransformComponent->GetWorldTM(); + } + + INetworkEntityManager* networkEntityManager = GetNetworkEntityManager(); + AZ_Assert(networkEntityManager != nullptr, + "Network Entity Manager must be initialized before NetworkSpawnableHolderComponent is activated"); + + m_netSpawnableTicket = networkEntityManager->RequestNetSpawnableInstantiation(m_networkSpawnableAsset, rootEntityTransform); + } } void NetworkSpawnableHolderComponent::Deactivate() { + m_netSpawnableTicket.reset(); } void NetworkSpawnableHolderComponent::SetNetworkSpawnableAsset(AZ::Data::Asset networkSpawnableAsset) @@ -39,7 +63,7 @@ namespace Multiplayer m_networkSpawnableAsset = networkSpawnableAsset; } - AZ::Data::Asset NetworkSpawnableHolderComponent::GetNetworkSpawnableAsset() + AZ::Data::Asset NetworkSpawnableHolderComponent::GetNetworkSpawnableAsset() const { return m_networkSpawnableAsset; } diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h index d369bbefd3..3836212c48 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace Multiplayer { @@ -33,9 +34,10 @@ namespace Multiplayer //! @} void SetNetworkSpawnableAsset(AZ::Data::Asset networkSpawnableAsset); - AZ::Data::Asset GetNetworkSpawnableAsset(); + AZ::Data::Asset GetNetworkSpawnableAsset() const; private: AZ::Data::Asset m_networkSpawnableAsset{ AZ::Data::AssetLoadBehavior::PreLoad }; + AZStd::unique_ptr m_netSpawnableTicket; }; } // namespace Multiplayer diff --git a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp new file mode 100644 index 0000000000..316f85c214 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp @@ -0,0 +1,390 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Multiplayer +{ + using namespace testing; + using namespace ::UnitTest; + + /* + * Test NetBindComponent activation. This must work before more complicated tests. + */ + TEST_F(HierarchyTests, On_Client_NetBindComponent_Activate) + { + AZStd::unique_ptr entity = AZStd::make_unique(); + entity->CreateComponent(); + SetupEntity(entity, NetEntityId{ 1 }, NetEntityRole::Client); + entity->Activate(); + + StopEntity(entity); + + entity->Deactivate(); + } + + /* + * Hierarchy test - a child entity on a client delaying activation until its hierarchical parent has been activated + */ + TEST_F(HierarchyTests, On_Client_EntityReplicator_DontActivate_BeforeParent) + { + // Create a child entity that will be tested for activation inside a hierarchy + AZStd::unique_ptr childEntity = AZStd::make_unique(); + CreateEntityWithChildHierarchy(childEntity); + SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client); + // child entity is not activated on purpose here, we are about to test conditional activation check + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(childEntity, NetEntityId{ 1 }); + SetHierarchyRootFieldOnNetworkHierarchyChild(childEntity, NetEntityId{ 1 }); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(childEntity.get(), m_networkEntityTracker.get()); + EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); + entityReplicator.Initialize(childHandle); + + // Entity replicator should not be ready to activate the entity because its parent does not exist + EXPECT_EQ(entityReplicator.IsReadyToActivate(), false); + } + + TEST_F(HierarchyTests, On_Client_EntityReplicator_DontActivate_Inner_Root_Before_Top_Root) + { + // Create a child entity that will be tested for activation inside a hierarchy + AZStd::unique_ptr innerRootEntity = AZStd::make_unique(); + CreateEntityWithRootHierarchy(innerRootEntity); + SetupEntity(innerRootEntity, NetEntityId{ 2 }, NetEntityRole::Client); + // child entity is not activated on purpose here, we are about to test conditional activation check + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(innerRootEntity, NetEntityId{ 1 }); + SetHierarchyRootFieldOnNetworkHierarchyChild(innerRootEntity, NetEntityId{ 1 }); + + // Create an entity replicator for the child entity + const NetworkEntityHandle innerRootHandle(innerRootEntity.get(), m_networkEntityTracker.get()); + EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, innerRootHandle); + entityReplicator.Initialize(innerRootHandle); + + // Entity replicator should not be ready to activate the entity because its parent does not exist + EXPECT_EQ(entityReplicator.IsReadyToActivate(), false); + } + + TEST_F(HierarchyTests, On_Client_Not_In_Hierarchy_EntityReplicator_Ignores_Parent) + { + // Create a child entity that will be tested for activation inside a hierarchy + AZStd::unique_ptr childEntity = AZStd::make_unique(); + CreateEntityWithChildHierarchy(childEntity); + SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client); + // child entity is not activated on purpose here, we are about to test conditional activation check + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(childEntity, NetEntityId{ 1 }); + SetHierarchyRootFieldOnNetworkHierarchyChild(childEntity, InvalidNetEntityId); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(childEntity.get(), m_networkEntityTracker.get()); + EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); + entityReplicator.Initialize(childHandle); + + // Entity replicator should not be ready to activate the entity because its parent does not exist + EXPECT_EQ(entityReplicator.IsReadyToActivate(), true); + } + + /* + * Hierarchy test - a child entity on a client allowing activation when its hierarchical parent is active + */ + TEST_F(HierarchyTests, On_Client_EntityReplicator_Activates_AfterParent) + { + AZStd::unique_ptr childEntity = AZStd::make_unique(); + CreateEntityWithChildHierarchy(childEntity); + SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client); + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(childEntity, NetEntityId{ 1 }); + SetHierarchyRootFieldOnNetworkHierarchyChild(childEntity, NetEntityId{ 1 }); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(childEntity.get(), m_networkEntityTracker.get()); + EntityReplicator childEntityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); + childEntityReplicator.Initialize(childHandle); + + // Now let's create a parent entity and activate it + AZStd::unique_ptr parentEntity = AZStd::make_unique(); + CreateEntityWithRootHierarchy(parentEntity); + SetupEntity(parentEntity, NetEntityId{ 1 }, NetEntityRole::Client); + + // Create an entity replicator for the parent entity + const NetworkEntityHandle parentHandle(parentEntity.get(), m_networkEntityTracker.get()); + ON_CALL(*m_mockNetworkEntityManager, GetEntity(_)).WillByDefault(Return(parentHandle)); + EntityReplicator parentEntityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, parentHandle); + parentEntityReplicator.Initialize(parentHandle); + + parentEntity->Activate(); + + // The child should be ready to be activated + EXPECT_EQ(childEntityReplicator.IsReadyToActivate(), true); + + StopEntity(parentEntity); + + parentEntity->Deactivate(); + } + + /* + * Parent -> Child + */ + class ClientSimpleHierarchyTests : public HierarchyTests + { + public: + const NetEntityId RootNetEntityId = NetEntityId{ 1 }; + const NetEntityId ChildNetEntityId = NetEntityId{ 2 }; + + void SetUp() override + { + HierarchyTests::SetUp(); + + m_root = AZStd::make_unique(1, "root", RootNetEntityId, EntityInfo::Role::Root); + m_child = AZStd::make_unique(2, "child", ChildNetEntityId, EntityInfo::Role::Child); + + CreateSimpleHierarchy(*m_root, *m_child); + + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + // now the two entities are under one hierarchy + } + + void TearDown() override + { + m_child.reset(); + m_root.reset(); + + HierarchyTests::TearDown(); + } + + void CreateSimpleHierarchy(EntityInfo& root, EntityInfo& child) + { + PopulateHierarchicalEntity(root); + SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Client); + + PopulateHierarchicalEntity(child); + SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Client); + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(child.m_entity, root.m_netId); + SetHierarchyRootFieldOnNetworkHierarchyChild(child.m_entity, root.m_netId); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle); + child.m_replicator->Initialize(childHandle); + + // Create an entity replicator for the root entity + const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get()); + root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, rootHandle); + root.m_replicator->Initialize(rootHandle); + + root.m_entity->Activate(); + child.m_entity->Activate(); + } + + void SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(const AZStd::unique_ptr& entity, NetEntityId value) + { + /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ + constexpr int totalBits = 1 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::Count*/; + constexpr int inHierarchyBit = 0 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::hierarchyRoot_DirtyFlag*/; + + ReplicationRecord currentRecord(NetEntityRole::Client); + currentRecord.m_authorityToClient.AddBits(totalBits); + currentRecord.m_authorityToClient.SetBit(inHierarchyBit, true); + + constexpr uint32_t bufferSize = 100; + AZStd::array buffer = {}; + NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); + inSerializer.Serialize(reinterpret_cast(value), + "hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ + AZStd::numeric_limits::min(), AZStd::numeric_limits::max()); + + NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); + + ReplicationRecord notifyRecord = currentRecord; + + entity->FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); + entity->FindComponent()->NotifyStateDeltaChanges(notifyRecord); + } + + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; + }; + + TEST_F(ClientSimpleHierarchyTests, Client_Activates_Hierarchy_From_Network_Fields) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchicalRoot(), + m_root->m_entity.get() + ); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 2) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() + ); + } + } + + TEST_F(ClientSimpleHierarchyTests, Client_Detaches_Child_When_Server_Detaches) + { + // simulate server detaching child entity + SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchicalRoot(), + nullptr + ); + } + + TEST_F(ClientSimpleHierarchyTests, Client_Sends_NetworkHierarchy_Updated_Event_On_Child_Detached_On_Server) + { + MockNetworkHierarchyCallbackHandler mock; + EXPECT_CALL(mock, OnNetworkHierarchyUpdated(m_root->m_entity->GetId())); + + m_root->m_entity->FindComponent()->BindNetworkHierarchyChangedEventHandler(mock.m_changedHandler); + + // simulate server detaching a child entity + SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId); + } + + TEST_F(ClientSimpleHierarchyTests, Client_Sends_NetworkHierarchy_Leave_Event_On_Child_Detached_On_Server) + { + MockNetworkHierarchyCallbackHandler mock; + EXPECT_CALL(mock, OnNetworkHierarchyLeave); + + m_child->m_entity->FindComponent()->BindNetworkHierarchyLeaveEventHandler(mock.m_leaveHandler); + + // simulate server detaching a child entity + SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId); + SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId); + } + + /* + * Parent -> Child -> ChildOfChild + */ + class ClientDeepHierarchyTests : public ClientSimpleHierarchyTests + { + public: + const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 }; + + void SetUp() override + { + ClientSimpleHierarchyTests::SetUp(); + + m_childOfChild = AZStd::make_unique((3), "child of child", ChildOfChildNetEntityId, EntityInfo::Role::Child); + + CreateDeepHierarchyOnClient(*m_childOfChild); + + m_childOfChild->m_entity->FindComponent()->SetParent(m_child->m_entity->GetId()); + } + + void TearDown() override + { + m_childOfChild.reset(); + + ClientSimpleHierarchyTests::TearDown(); + } + + void CreateDeepHierarchyOnClient(EntityInfo& childOfChild) + { + PopulateHierarchicalEntity(childOfChild); + SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Client); + + // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller) + SetParentIdOnNetworkTransform(childOfChild.m_entity, m_childOfChild->m_netId); + SetHierarchyRootFieldOnNetworkHierarchyChild(childOfChild.m_entity, m_root->m_netId); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childOfChildHandle(childOfChild.m_entity.get(), m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childOfChildHandle); + childOfChild.m_replicator->Initialize(childOfChildHandle); + + childOfChild.m_entity->Activate(); + } + + AZStd::unique_ptr m_childOfChild; + }; + + TEST_F(ClientDeepHierarchyTests, Client_Activates_Hierarchy_From_Network_Fields) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + EXPECT_EQ( + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchicalRoot(), + m_root->m_entity.get() + ); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild->m_entity.get() + ); + } + } +} diff --git a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h new file mode 100644 index 0000000000..2deac1aa27 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h @@ -0,0 +1,414 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Multiplayer +{ + using namespace testing; + using namespace ::UnitTest; + + class NetworkHierarchyCallbacks + { + public: + virtual ~NetworkHierarchyCallbacks() = default; + virtual void OnNetworkHierarchyLeave() = 0; + virtual void OnNetworkHierarchyUpdated(const AZ::EntityId& hierarchyRootId) = 0; + }; + + class MockNetworkHierarchyCallbackHandler : public NetworkHierarchyCallbacks + { + public: + MockNetworkHierarchyCallbackHandler() + : m_leaveHandler([this]() { OnNetworkHierarchyLeave(); }) + , m_changedHandler([this](const AZ::EntityId& rootId) { OnNetworkHierarchyUpdated(rootId); }) + { + } + + NetworkHierarchyLeaveEvent::Handler m_leaveHandler; + NetworkHierarchyChangedEvent::Handler m_changedHandler; + + MOCK_METHOD0(OnNetworkHierarchyLeave, void()); + MOCK_METHOD1(OnNetworkHierarchyUpdated, void(const AZ::EntityId&)); + }; + + class HierarchyTests + : public AllocatorsFixture + { + public: + void SetUp() override + { + SetupAllocator(); + AZ::NameDictionary::Create(); + + m_mockComponentApplicationRequests = AZStd::make_unique>(); + AZ::Interface::Register(m_mockComponentApplicationRequests.get()); + + ON_CALL(*m_mockComponentApplicationRequests, AddEntity(_)).WillByDefault(Invoke(this, &HierarchyTests::AddEntity)); + ON_CALL(*m_mockComponentApplicationRequests, FindEntity(_)).WillByDefault(Invoke(this, &HierarchyTests::FindEntity)); + + // register components involved in testing + m_serializeContext = AZStd::make_unique(); + + m_transformDescriptor.reset(AzFramework::TransformComponent::CreateDescriptor()); + m_transformDescriptor->Reflect(m_serializeContext.get()); + + m_netBindDescriptor.reset(NetBindComponent::CreateDescriptor()); + m_netBindDescriptor->Reflect(m_serializeContext.get()); + + m_hierarchyRootDescriptor.reset(NetworkHierarchyRootComponent::CreateDescriptor()); + m_hierarchyRootDescriptor->Reflect(m_serializeContext.get()); + + m_hierarchyChildDescriptor.reset(NetworkHierarchyChildComponent::CreateDescriptor()); + m_hierarchyChildDescriptor->Reflect(m_serializeContext.get()); + + m_netTransformDescriptor.reset(NetworkTransformComponent::CreateDescriptor()); + m_netTransformDescriptor->Reflect(m_serializeContext.get()); + + m_mockMultiplayer = AZStd::make_unique>(); + AZ::Interface::Register(m_mockMultiplayer.get()); + + EXPECT_NE(AZ::Interface::Get(), nullptr); + + // Create space for replication stats + // Without Multiplayer::RegisterMultiplayerComponents() the stats go to invalid id, which is fine for unit tests + GetMultiplayer()->GetStats().ReserveComponentStats(Multiplayer::InvalidNetComponentId, 50, 0); + + m_mockNetworkEntityManager = AZStd::make_unique>(); + + ON_CALL(*m_mockNetworkEntityManager, AddEntityToEntityMap(_, _)).WillByDefault(Invoke(this, &HierarchyTests::AddEntityToEntityMap)); + ON_CALL(*m_mockNetworkEntityManager, GetEntity(_)).WillByDefault(Invoke(this, &HierarchyTests::GetEntity)); + ON_CALL(*m_mockNetworkEntityManager, GetNetEntityIdById(_)).WillByDefault(Invoke(this, &HierarchyTests::GetNetEntityIdById)); + + m_mockTime = AZStd::make_unique>(); + AZ::Interface::Register(m_mockTime.get()); + + m_mockNetworkTime = AZStd::make_unique>(); + AZ::Interface::Register(m_mockNetworkTime.get()); + + ON_CALL(*m_mockMultiplayer, GetNetworkEntityManager()).WillByDefault(Return(m_mockNetworkEntityManager.get())); + EXPECT_NE(AZ::Interface::Get()->GetNetworkEntityManager(), nullptr); + + const IpAddress address("localhost", 1, ProtocolType::Udp); + m_mockConnection = AZStd::make_unique>(ConnectionId{ 1 }, address, ConnectionRole::Connector); + m_mockConnectionListener = AZStd::make_unique(); + + m_networkEntityTracker = AZStd::make_unique(); + ON_CALL(*m_mockNetworkEntityManager, GetNetworkEntityTracker()).WillByDefault(Return(m_networkEntityTracker.get())); + + m_networkEntityAuthorityTracker = AZStd::make_unique(*m_mockNetworkEntityManager); + ON_CALL(*m_mockNetworkEntityManager, GetNetworkEntityAuthorityTracker()).WillByDefault(Return(m_networkEntityAuthorityTracker.get())); + + m_entityReplicationManager = AZStd::make_unique(*m_mockConnection, *m_mockConnectionListener, EntityReplicationManager::Mode::LocalClientToRemoteServer); + + m_console.reset(aznew AZ::Console()); + AZ::Interface::Register(m_console.get()); + m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead()); + + m_multiplayerComponentRegistry = AZStd::make_unique(); + ON_CALL(*m_mockNetworkEntityManager, GetMultiplayerComponentRegistry()).WillByDefault(Return(m_multiplayerComponentRegistry.get())); + RegisterMultiplayerComponents(); + } + + void TearDown() override + { + m_multiplayerComponentRegistry.reset(); + + AZ::Interface::Unregister(m_console.get()); + m_console.reset(); + + m_networkEntityMap.clear(); + m_entities.clear(); + + m_entityReplicationManager.reset(); + + m_mockConnection.reset(); + m_mockConnectionListener.reset(); + m_networkEntityTracker.reset(); + m_networkEntityAuthorityTracker.reset(); + + AZ::Interface::Unregister(m_mockNetworkTime.get()); + AZ::Interface::Unregister(m_mockTime.get()); + AZ::Interface::Unregister(m_mockMultiplayer.get()); + AZ::Interface::Unregister(m_mockComponentApplicationRequests.get()); + + m_mockTime.reset(); + + m_mockNetworkEntityManager.reset(); + m_mockMultiplayer.reset(); + + m_transformDescriptor.reset(); + m_netTransformDescriptor.reset(); + m_hierarchyRootDescriptor.reset(); + m_hierarchyChildDescriptor.reset(); + m_netBindDescriptor.reset(); + m_serializeContext.reset(); + m_mockComponentApplicationRequests.reset(); + + AZ::NameDictionary::Destroy(); + TeardownAllocator(); + } + + AZStd::unique_ptr m_console; + + AZStd::unique_ptr> m_mockComponentApplicationRequests; + AZStd::unique_ptr m_serializeContext; + AZStd::unique_ptr m_transformDescriptor; + AZStd::unique_ptr m_netBindDescriptor; + AZStd::unique_ptr m_hierarchyRootDescriptor; + AZStd::unique_ptr m_hierarchyChildDescriptor; + AZStd::unique_ptr m_netTransformDescriptor; + + AZStd::unique_ptr> m_mockMultiplayer; + AZStd::unique_ptr m_mockNetworkEntityManager; + AZStd::unique_ptr> m_mockTime; + AZStd::unique_ptr> m_mockNetworkTime; + + AZStd::unique_ptr> m_mockConnection; + AZStd::unique_ptr m_mockConnectionListener; + AZStd::unique_ptr m_networkEntityTracker; + AZStd::unique_ptr m_networkEntityAuthorityTracker; + + AZStd::unique_ptr m_entityReplicationManager; + + AZStd::unique_ptr m_multiplayerComponentRegistry;; + + mutable AZStd::map m_networkEntityMap; + + NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) + { + m_networkEntityMap[netEntityId] = entity; + return NetworkEntityHandle(entity, netEntityId, m_networkEntityTracker.get()); + } + + ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const + { + AZ::Entity* entity = m_networkEntityMap[netEntityId]; + return ConstNetworkEntityHandle(entity, m_networkEntityTracker.get()); + } + + NetEntityId GetNetEntityIdById(const AZ::EntityId& entityId) const + { + for (const auto& pair : m_networkEntityMap) + { + if (pair.second->GetId() == entityId) + { + return pair.first; + } + } + + return InvalidNetEntityId; + } + + AZStd::map m_entities; + + bool AddEntity(AZ::Entity* entity) + { + m_entities[entity->GetId()] = entity; + return true; + } + + AZ::Entity* FindEntity(AZ::EntityId entityId) + { + const auto iterator = m_entities.find(entityId); + if (iterator != m_entities.end()) + { + return iterator->second; + } + + return nullptr; + } + + void SetupEntity(const AZStd::unique_ptr& entity, NetEntityId netId, NetEntityRole role) + { + const auto netBindComponent = entity->FindComponent(); + EXPECT_NE(netBindComponent, nullptr); + netBindComponent->PreInit(entity.get(), PrefabEntityId{ AZ::Name("test"), 1 }, netId, role); + entity->Init(); + } + + static void StopEntity(const AZStd::unique_ptr& entity) + { + const auto netBindComponent = entity->FindComponent(); + EXPECT_NE(netBindComponent, nullptr); + netBindComponent->StopEntity(); + } + + static void StopAndDeactivateEntity(AZStd::unique_ptr& entity) + { + if (entity) + { + StopEntity(entity); + entity->Deactivate(); + entity.reset(); + } + } + + void CreateEntityWithRootHierarchy(AZStd::unique_ptr& rootEntity) + { + rootEntity->CreateComponent(); + rootEntity->CreateComponent(); + rootEntity->CreateComponent(); + rootEntity->CreateComponent(); + } + + void CreateEntityWithChildHierarchy(AZStd::unique_ptr& childEntity) + { + childEntity->CreateComponent(); + childEntity->CreateComponent(); + childEntity->CreateComponent(); + childEntity->CreateComponent(); + } + + void SetParentIdOnNetworkTransform(const AZStd::unique_ptr& entity, NetEntityId netParentId) + { + /* Derived from NetworkTransformComponent.AutoComponent.xml */ + constexpr int totalBits = 6 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::Count*/; + constexpr int parentIdBit = 4 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::parentEntityId_DirtyFlag*/; + + ReplicationRecord currentRecord; + currentRecord.m_authorityToClient.AddBits(totalBits); + currentRecord.m_authorityToClient.SetBit(parentIdBit, true); + + constexpr uint32_t bufferSize = 100; + AZStd::array buffer = {}; + NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); + inSerializer.Serialize(reinterpret_cast(netParentId), + "parentEntityId", /* Derived from NetworkTransformComponent.AutoComponent.xml */ + AZStd::numeric_limits::min(), AZStd::numeric_limits::max()); + + NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); + + ReplicationRecord notifyRecord = currentRecord; + entity->FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); + entity->FindComponent()->NotifyStateDeltaChanges(notifyRecord); + } + + template + void SetHierarchyRootFieldOnNetworkHierarchyChild(const AZStd::unique_ptr& entity, NetEntityId value) + { + /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ + constexpr int totalBits = 1 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::Count*/; + constexpr int inHierarchyBit = 0 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::hierarchyRoot_DirtyFlag*/; + + ReplicationRecord currentRecord; + currentRecord.m_authorityToClient.AddBits(totalBits); + currentRecord.m_authorityToClient.SetBit(inHierarchyBit, true); + + constexpr uint32_t bufferSize = 100; + AZStd::array buffer = {}; + NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); + inSerializer.Serialize(reinterpret_cast(value), + "hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ + AZStd::numeric_limits::min(), AZStd::numeric_limits::max()); + + NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); + + ReplicationRecord notifyRecord = currentRecord; + entity->FindComponent()->SerializeStateDeltaMessage(currentRecord, outSerializer); + entity->FindComponent()->NotifyStateDeltaChanges(notifyRecord); + } + + struct EntityInfo + { + enum class Role + { + Root, + Child, + None + }; + + EntityInfo(AZ::u64 entityId, const char* entityName, NetEntityId netId, Role role) + : m_entity(AZStd::make_unique(AZ::EntityId(entityId), entityName)) + , m_netId(netId) + , m_role(role) + { + } + + ~EntityInfo() + { + StopAndDeactivateEntity(m_entity); + } + + AZStd::unique_ptr m_entity; + NetEntityId m_netId; + AZStd::unique_ptr m_replicator; + Role m_role = Role::None; + }; + + void PopulateHierarchicalEntity(const EntityInfo& entityInfo) + { + entityInfo.m_entity->CreateComponent(); + entityInfo.m_entity->CreateComponent(); + entityInfo.m_entity->CreateComponent(); + switch (entityInfo.m_role) + { + case EntityInfo::Role::Root: + entityInfo.m_entity->CreateComponent(); + break; + case EntityInfo::Role::Child: + entityInfo.m_entity->CreateComponent(); + break; + case EntityInfo::Role::None: + break; + } + } + + void CreateDeepHierarchy(EntityInfo& root, EntityInfo& child, EntityInfo& childOfChild) + { + PopulateHierarchicalEntity(root); + PopulateHierarchicalEntity(child); + PopulateHierarchicalEntity(childOfChild); + + SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Authority); + SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority); + SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Authority); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childOfChildHandle(childOfChild.m_entity.get(), m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childOfChildHandle); + childOfChild.m_replicator->Initialize(childOfChildHandle); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childHandle); + child.m_replicator->Initialize(childHandle); + + // Create an entity replicator for the root entity + const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get()); + root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, rootHandle); + root.m_replicator->Initialize(rootHandle); + + root.m_entity->Activate(); + child.m_entity->Activate(); + childOfChild.m_entity->Activate(); + } + }; +} diff --git a/Gems/Multiplayer/Code/Tests/MainTools.cpp b/Gems/Multiplayer/Code/Tests/MainTools.cpp index 89b2492bc6..53dfad62d1 100644 --- a/Gems/Multiplayer/Code/Tests/MainTools.cpp +++ b/Gems/Multiplayer/Code/Tests/MainTools.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -30,7 +29,6 @@ namespace Multiplayer { AZStd::vector descriptors({ NetBindComponent::CreateDescriptor(), - NetBindMarkerComponent::CreateDescriptor(), NetworkSpawnableHolderComponent::CreateDescriptor() }); diff --git a/Gems/Multiplayer/Code/Tests/MockInterfaces.h b/Gems/Multiplayer/Code/Tests/MockInterfaces.h new file mode 100644 index 0000000000..42e034d234 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/MockInterfaces.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + class MockMultiplayer : public Multiplayer::IMultiplayer + { + public: + MOCK_CONST_METHOD0(GetCurrentBlendFactor, float ()); + MOCK_CONST_METHOD0(GetAgentType, Multiplayer::MultiplayerAgentType()); + MOCK_METHOD1(InitializeMultiplayer, void(Multiplayer::MultiplayerAgentType)); + MOCK_METHOD2(StartHosting, bool(uint16_t, bool)); + MOCK_METHOD2(Connect, bool(AZStd::string, uint16_t)); + MOCK_METHOD1(Terminate, void(AzNetworking::DisconnectReason)); + MOCK_METHOD1(AddClientDisconnectedHandler, void(AZ::Event<>::Handler&)); + MOCK_METHOD1(AddConnectionAcquiredHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(AddSessionInitHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(AddSessionShutdownHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(SendReadyForEntityUpdates, void(bool)); + MOCK_CONST_METHOD0(GetCurrentHostTimeMs, AZ::TimeMs()); + MOCK_METHOD0(GetNetworkTime, Multiplayer::INetworkTime* ()); + MOCK_METHOD0(GetNetworkEntityManager, Multiplayer::INetworkEntityManager* ()); + MOCK_METHOD1(SetFilterEntityManager, void(Multiplayer::IFilterEntityManager*)); + MOCK_METHOD0(GetFilterEntityManager, Multiplayer::IFilterEntityManager* ()); + }; + + class MockNetworkEntityManager : public Multiplayer::INetworkEntityManager + { + public: + MOCK_METHOD2(RequestNetSpawnableInstantiation, AZStd::unique_ptr (const AZ::Data::Asset&, const AZ::Transform&)); + MOCK_METHOD4( + CreateEntitiesImmediate, + EntityList (const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityRole, const AZ::Transform&, Multiplayer::AutoActivate)); + MOCK_CONST_METHOD1(GetNetEntityIdById, Multiplayer::NetEntityId (const AZ::EntityId&)); + MOCK_METHOD0(GetNetworkEntityTracker, Multiplayer::NetworkEntityTracker* ()); + MOCK_METHOD0(GetNetworkEntityAuthorityTracker, Multiplayer::NetworkEntityAuthorityTracker* ()); + MOCK_METHOD0(GetMultiplayerComponentRegistry, Multiplayer::MultiplayerComponentRegistry* ()); + MOCK_CONST_METHOD0(GetHostId, Multiplayer::HostId()); + MOCK_METHOD3(CreateEntitiesImmediate, EntityList(const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityRole, const AZ:: + Transform&)); + MOCK_METHOD5(CreateEntitiesImmediate, EntityList(const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityId, Multiplayer:: + NetEntityRole, Multiplayer::AutoActivate, const AZ::Transform&)); + MOCK_METHOD3(SetupNetEntity, void(AZ::Entity*, Multiplayer::PrefabEntityId, Multiplayer::NetEntityRole)); + MOCK_CONST_METHOD1(GetEntity, Multiplayer::ConstNetworkEntityHandle(Multiplayer::NetEntityId)); + MOCK_CONST_METHOD0(GetEntityCount, uint32_t()); + MOCK_METHOD2(AddEntityToEntityMap, Multiplayer::NetworkEntityHandle(Multiplayer::NetEntityId, AZ::Entity*)); + MOCK_METHOD1(MarkForRemoval, void(const Multiplayer::ConstNetworkEntityHandle&)); + MOCK_CONST_METHOD1(IsMarkedForRemoval, bool(const Multiplayer::ConstNetworkEntityHandle&)); + MOCK_METHOD1(ClearEntityFromRemovalList, void(const Multiplayer::ConstNetworkEntityHandle&)); + MOCK_METHOD0(ClearAllEntities, void()); + MOCK_METHOD1(AddEntityMarkedDirtyHandler, void(AZ::Event<>::Handler&)); + MOCK_METHOD1(AddEntityNotifyChangesHandler, void(AZ::Event<>::Handler&)); + MOCK_METHOD1(AddEntityExitDomainHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(AddControllersActivatedHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(AddControllersDeactivatedHandler, void(AZ::Event::Handler&)); + MOCK_METHOD0(NotifyEntitiesDirtied, void()); + MOCK_METHOD0(NotifyEntitiesChanged, void()); + MOCK_METHOD2(NotifyControllersActivated, void(const Multiplayer::ConstNetworkEntityHandle&, Multiplayer::EntityIsMigrating)); + MOCK_METHOD2(NotifyControllersDeactivated, void(const Multiplayer::ConstNetworkEntityHandle&, Multiplayer::EntityIsMigrating)); + MOCK_METHOD1(HandleLocalRpcMessage, void(Multiplayer::NetworkEntityRpcMessage&)); + }; + + class MockConnectionListener : public AzNetworking::IConnectionListener + { + public: + MOCK_METHOD3(ValidateConnect, ConnectResult(const IpAddress&, const IPacketHeader&, ISerializer&)); + MOCK_METHOD1(OnConnect, void(IConnection*)); + MOCK_METHOD3(OnPacketReceived, PacketDispatchResult (IConnection*, const IPacketHeader&, ISerializer&)); + MOCK_METHOD2(OnPacketLost, void(IConnection*, PacketId)); + MOCK_METHOD3(OnDisconnect, void(IConnection*, DisconnectReason, TerminationEndpoint)); + }; + + class MockTime : public AZ::ITime + { + public: + MOCK_CONST_METHOD0(GetElapsedTimeMs, AZ::TimeMs()); + }; + + class MockNetworkTime : public Multiplayer::INetworkTime + { + public: + MOCK_METHOD2(ForceSetTime, void (Multiplayer::HostFrameId, AZ::TimeMs)); + MOCK_CONST_METHOD0(GetHostBlendFactor, float ()); + MOCK_METHOD1(AlterBlendFactor, void (float)); + MOCK_CONST_METHOD0(IsTimeRewound, bool()); + MOCK_CONST_METHOD0(GetHostFrameId, Multiplayer::HostFrameId()); + MOCK_CONST_METHOD0(GetUnalteredHostFrameId, Multiplayer::HostFrameId()); + MOCK_METHOD0(IncrementHostFrameId, void()); + MOCK_CONST_METHOD0(GetHostTimeMs, AZ::TimeMs()); + MOCK_CONST_METHOD0(GetRewindingConnectionId, AzNetworking::ConnectionId()); + MOCK_CONST_METHOD1(GetHostFrameIdForRewindingConnection, Multiplayer::HostFrameId(AzNetworking::ConnectionId)); + MOCK_METHOD4(AlterTime, void (Multiplayer::HostFrameId, AZ::TimeMs, float, AzNetworking::ConnectionId)); + MOCK_METHOD1(SyncEntitiesToRewindState, void(const AZ::Aabb&)); + MOCK_METHOD0(ClearRewoundEntities, void()); + }; + + class MockComponentApplicationRequests : public AZ::ComponentApplicationRequests + { + public: + MOCK_METHOD1(RegisterComponentDescriptor, void(const AZ::ComponentDescriptor*)); + MOCK_METHOD1(UnregisterComponentDescriptor, void(const AZ::ComponentDescriptor*)); + MOCK_METHOD0(GetApplication, AZ::ComponentApplication* ()); + MOCK_METHOD1(RegisterEntityAddedEventHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(RegisterEntityRemovedEventHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(RegisterEntityActivatedEventHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(RegisterEntityDeactivatedEventHandler, void(AZ::Event::Handler&)); + MOCK_METHOD1(SignalEntityActivated, void(AZ::Entity*)); + MOCK_METHOD1(SignalEntityDeactivated, void(AZ::Entity*)); + MOCK_METHOD1(AddEntity, bool(AZ::Entity*)); + MOCK_METHOD1(RemoveEntity, bool(AZ::Entity*)); + MOCK_METHOD1(DeleteEntity, bool(const AZ::EntityId&)); + MOCK_METHOD1(FindEntity, AZ::Entity* (const AZ::EntityId&)); + MOCK_METHOD1(GetEntityName, AZStd::string(const AZ::EntityId&)); + MOCK_METHOD1(EnumerateEntities, void(const EntityCallback&)); + MOCK_METHOD0(GetSerializeContext, AZ::SerializeContext* ()); + MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ()); + MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ()); + MOCK_CONST_METHOD0(GetAppRoot, const char* ()); + MOCK_CONST_METHOD0(GetEngineRoot, const char* ()); + MOCK_CONST_METHOD0(GetExecutableFolder, const char* ()); + MOCK_METHOD0(GetDrillerManager, AZ::Debug::DrillerManager* ()); + MOCK_METHOD1(ResolveModulePath, void(AZ::OSString&)); + MOCK_METHOD0(GetAzCommandLine, AZ::CommandLine* ()); + MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&)); + }; + + class MockSerializer : public ISerializer + { + public: + MOCK_CONST_METHOD0(IsValid, bool ()); + MOCK_CONST_METHOD0(GetSerializerMode, SerializerMode ()); + MOCK_METHOD2(Serialize, bool (bool&, const char*)); + MOCK_METHOD4(Serialize, bool (char&, const char*, char, char)); + MOCK_METHOD4(Serialize, bool (int8_t&, const char*, int8_t, int8_t)); + MOCK_METHOD4(Serialize, bool (int16_t&, const char*, int16_t, int16_t)); + MOCK_METHOD4(Serialize, bool (int32_t&, const char*, int32_t, int32_t)); + MOCK_METHOD4(Serialize, bool (int64_t&, const char*, int64_t, int64_t)); + MOCK_METHOD4(Serialize, bool (uint8_t&, const char*, uint8_t, uint8_t)); + MOCK_METHOD4(Serialize, bool (uint16_t&, const char*, uint16_t, uint16_t)); + MOCK_METHOD4(Serialize, bool (uint32_t&, const char*, uint32_t, uint32_t)); + MOCK_METHOD4(Serialize, bool (uint64_t&, const char*, uint64_t, uint64_t)); + MOCK_METHOD4(Serialize, bool (float&, const char*, float, float)); + MOCK_METHOD4(Serialize, bool (double&, const char*, double, double)); + MOCK_METHOD5(SerializeBytes, bool (uint8_t*, uint32_t, bool, uint32_t&, const char*)); + MOCK_METHOD2(BeginObject, bool (const char*, const char*)); + MOCK_METHOD2(EndObject, bool (const char*, const char*)); + MOCK_CONST_METHOD0(GetBuffer, const uint8_t* ()); + MOCK_CONST_METHOD0(GetCapacity, uint32_t ()); + MOCK_CONST_METHOD0(GetSize, uint32_t ()); + MOCK_METHOD0(ClearTrackedChangesFlag, void ()); + MOCK_CONST_METHOD0(GetTrackedChangesFlag, bool ()); + }; +} + diff --git a/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp new file mode 100644 index 0000000000..385a4b83ae --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/ServerHierarchyTests.cpp @@ -0,0 +1,1233 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Multiplayer +{ + using namespace testing; + using namespace ::UnitTest; + + /* + * Parent -> Child + */ + class ServerSimpleHierarchyTests : public HierarchyTests + { + public: + void SetUp() override + { + HierarchyTests::SetUp(); + + m_root = AZStd::make_unique(1, "root", NetEntityId{ 1 }, EntityInfo::Role::Root); + m_child = AZStd::make_unique(2, "child", NetEntityId{ 2 }, EntityInfo::Role::Child); + + CreateSimpleHierarchy(*m_root, *m_child); + + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + // now the two entities are under one hierarchy + } + + void TearDown() override + { + m_child.reset(); + m_root.reset(); + + HierarchyTests::TearDown(); + } + + void CreateSimpleHierarchy(EntityInfo& root, EntityInfo& child) + { + PopulateHierarchicalEntity(root); + SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Authority); + + PopulateHierarchicalEntity(child); + SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority); + + // Create an entity replicator for the child entity + const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childHandle); + child.m_replicator->Initialize(childHandle); + + // Create an entity replicator for the root entity + const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get()); + root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, rootHandle); + root.m_replicator->Initialize(rootHandle); + + root.m_entity->Activate(); + child.m_entity->Activate(); + } + + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; + }; + + TEST_F(ServerSimpleHierarchyTests, Server_Sets_Appropriate_Network_Fields_For_Clients) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + NetEntityId{ 1 } + ); + } + + TEST_F(ServerSimpleHierarchyTests, Root_Is_Top_Level_Root) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->IsHierarchicalChild(), + false + ); + } + + TEST_F(ServerSimpleHierarchyTests, Child_Has_Root_Set) + { + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + NetEntityId{ 1 } + ); + } + + TEST_F(ServerSimpleHierarchyTests, Child_Has_Root_Cleared_On_Detach) + { + // now detach the child + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerSimpleHierarchyTests, Root_Has_Child_Reference) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerSimpleHierarchyTests, Root_Has_Child_References_Removed_On_Detach) + { + // now detach the child + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + TEST_F(ServerSimpleHierarchyTests, Root_Deactivates_Child_Has_No_References_To_Root) + { + StopEntity(m_root->m_entity); + m_root->m_entity->Deactivate(); + m_root->m_entity.reset(); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerSimpleHierarchyTests, Child_Deactivates_Root_Has_No_References_To_Child) + { + m_child.reset(); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + TEST_F(ServerSimpleHierarchyTests, Root_Deactivates_IsHierarchyEnabled_Is_False) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->IsHierarchyEnabled(), + true + ); + + StopEntity(m_root->m_entity); + m_root->m_entity->Deactivate(); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->IsHierarchyEnabled(), + false + ); + + m_root->m_entity.reset(); + } + + TEST_F(ServerSimpleHierarchyTests, Child_Deactivates_IsHierarchyEnabled_Is_False) + { + EXPECT_EQ( + m_child->m_entity->FindComponent()->IsHierarchyEnabled(), + true + ); + + StopEntity(m_child->m_entity); + m_child->m_entity->Deactivate(); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->IsHierarchyEnabled(), + false + ); + + m_child->m_entity.reset(); + } + + /* + * Parent -> Child -> ChildOfChild + */ + class ServerDeepHierarchyTests : public HierarchyTests + { + public: + const NetEntityId RootNetEntityId = NetEntityId{ 1 }; + const NetEntityId ChildNetEntityId = NetEntityId{ 2 }; + const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 }; + + void SetUp() override + { + HierarchyTests::SetUp(); + + m_root = AZStd::make_unique((1), "root", RootNetEntityId, EntityInfo::Role::Root); + m_child = AZStd::make_unique((2), "child", ChildNetEntityId, EntityInfo::Role::Child); + m_childOfChild = AZStd::make_unique((3), "child of child", ChildOfChildNetEntityId, EntityInfo::Role::Child); + + CreateDeepHierarchy(*m_root, *m_child, *m_childOfChild); + + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_child->m_entity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChild.reset(); + m_child.reset(); + m_root.reset(); + + HierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; + AZStd::unique_ptr m_childOfChild; + }; + + TEST_F(ServerDeepHierarchyTests, Root_Is_Top_Level_Root) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->IsHierarchicalChild(), + false + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_Child_References) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild->m_entity.get() + ); + } + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_Child_Of_Child_Reference_Removed_On_Detach) + { + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_All_References_Removed_On_Detach_Of_Mid_Child) + { + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_All_References_If_Mid_Child_Added_With_Child) + { + m_root->m_entity->FindComponent()->SetParent(AZ::EntityId()); + // reconnect + m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Has_All_References_If_Child_Of_Child_Added) + { + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); + // reconnect + m_childOfChild->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerDeepHierarchyTests, Child_Of_Child_Points_To_Root_After_Attach) + { + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); + // reconnect + m_childOfChild->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, All_New_Children_Point_To_Root_If_Mid_Child_Added_With_Child) + { + m_root->m_entity->FindComponent()->SetParent(AZ::EntityId()); + // reconnect + m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + EXPECT_EQ( + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, Children_Clear_Reference_To_Root_After_Mid_Child_Detached) + { + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + EXPECT_EQ( + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, Child_Of_Child_Clears_Reference_To_Root_After_Detached) + { + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, Root_Deactivates_Children_Have_No_References_To_Root) + { + m_root.reset(); + + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + + EXPECT_EQ( + m_childOfChild->m_entity->FindComponent()->GetHierarchyRoot(), + InvalidNetEntityId + ); + } + + TEST_F(ServerDeepHierarchyTests, Child_Of_Child_Deactivates_Root_Removes_References_To_It) + { + m_childOfChild.reset(); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerDeepHierarchyTests, Testing_Limiting_Hierarchy_Maximum_Size) + { + uint32_t currentMaxLimit = 0; + m_console->GetCvarValue("bg_hierarchyEntityMaxLimit", currentMaxLimit); + m_console->PerformCommand("bg_hierarchyEntityMaxLimit 2"); + + // remake the hierarchy + m_root->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + + m_console->PerformCommand((AZStd::string("bg_hierarchyEntityMaxLimit ") + AZStd::to_string(currentMaxLimit)).c_str()); + m_console->GetCvarValue("bg_hierarchyEntityMaxLimit", currentMaxLimit); + } + + /* + * Parent -> Child -> Child Of Child + * -> Child2 -> Child Of Child2 + * -> Child2 Of Child2 + */ + class ServerBranchedHierarchyTests : public HierarchyTests + { + public: + const NetEntityId RootNetEntityId = NetEntityId{ 1 }; + const NetEntityId ChildNetEntityId = NetEntityId{ 2 }; + const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 }; + const NetEntityId Child2NetEntityId = NetEntityId{ 4 }; + const NetEntityId ChildOfChild2NetEntityId = NetEntityId{ 5 }; + const NetEntityId Child2OfChild2NetEntityId = NetEntityId{ 6 }; + + void SetUp() override + { + HierarchyTests::SetUp(); + + m_root = AZStd::make_unique((1), "root", RootNetEntityId, EntityInfo::Role::Root); + m_child = AZStd::make_unique((2), "child", ChildNetEntityId, EntityInfo::Role::Child); + m_childOfChild = AZStd::make_unique((3), "child of child", ChildOfChildNetEntityId, EntityInfo::Role::Child); + m_child2 = AZStd::make_unique((4), "child2", Child2NetEntityId, EntityInfo::Role::Child); + m_childOfChild2 = AZStd::make_unique((5), "child of child2", ChildOfChild2NetEntityId, EntityInfo::Role::Child); + m_child2OfChild2 = AZStd::make_unique((6), "child2 of child2", Child2OfChild2NetEntityId, EntityInfo::Role::Child); + + CreateBranchedHierarchy(*m_root, *m_child, *m_childOfChild, + *m_child2, *m_childOfChild2, *m_child2OfChild2); + + m_child2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_childOfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); + m_child2OfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_child->m_entity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_child2OfChild2.reset(); + m_childOfChild2.reset(); + m_child2.reset(); + m_childOfChild.reset(); + m_child.reset(); + m_root.reset(); + + HierarchyTests::TearDown(); + } + + + void CreateBranchedHierarchy(EntityInfo& root, EntityInfo& child, EntityInfo& childOfChild, + EntityInfo& child2, EntityInfo& childOfChild2, EntityInfo& child2OfChild2) + { + PopulateHierarchicalEntity(root); + PopulateHierarchicalEntity(child); + PopulateHierarchicalEntity(childOfChild); + PopulateHierarchicalEntity(child2); + PopulateHierarchicalEntity(childOfChild2); + PopulateHierarchicalEntity(child2OfChild2); + + SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Authority); + SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority); + SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Authority); + SetupEntity(child2.m_entity, child2.m_netId, NetEntityRole::Authority); + SetupEntity(childOfChild2.m_entity, childOfChild2.m_netId, NetEntityRole::Authority); + SetupEntity(child2OfChild2.m_entity, child2OfChild2.m_netId, NetEntityRole::Authority); + + // Create entity replicators + const NetworkEntityHandle childOfChild2Handle(childOfChild2.m_entity.get(), m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childOfChild2Handle); + childOfChild.m_replicator->Initialize(childOfChild2Handle); + + const NetworkEntityHandle child2OfChild2Handle(child2OfChild2.m_entity.get(), m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, child2OfChild2Handle); + childOfChild.m_replicator->Initialize(child2OfChild2Handle); + + const NetworkEntityHandle child2Handle(child2.m_entity.get(), m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, child2Handle); + child.m_replicator->Initialize(child2Handle); + + const NetworkEntityHandle childOfChildHandle(childOfChild.m_entity.get(), m_networkEntityTracker.get()); + childOfChild.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childOfChildHandle); + childOfChild.m_replicator->Initialize(childOfChildHandle); + + const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get()); + child.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, childHandle); + child.m_replicator->Initialize(childHandle); + + const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get()); + root.m_replicator = AZStd::make_unique(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Client, rootHandle); + root.m_replicator->Initialize(rootHandle); + + root.m_entity->Activate(); + child.m_entity->Activate(); + childOfChild.m_entity->Activate(); + child2.m_entity->Activate(); + childOfChild2.m_entity->Activate(); + child2OfChild2.m_entity->Activate(); + } + + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; + AZStd::unique_ptr m_childOfChild; + AZStd::unique_ptr m_child2; + AZStd::unique_ptr m_childOfChild2; + AZStd::unique_ptr m_child2OfChild2; + }; + + TEST_F(ServerBranchedHierarchyTests, Sanity_Check) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 6) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[3], + m_child2->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[4], + m_child2OfChild2->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[5], + m_childOfChild2->m_entity.get() + ); + } + } + + TEST_F(ServerBranchedHierarchyTests, Detach_Child_While_Child2_Remains_Attached) + { + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 4 + ); + + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 4) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child2->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_child2OfChild2->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[3], + m_childOfChild2->m_entity.get() + ); + } + + EXPECT_EQ( + m_child2->m_entity->FindComponent()->GetHierarchicalRoot(), + m_root->m_entity.get() + ); + EXPECT_EQ( + m_child->m_entity->FindComponent()->GetHierarchicalRoot(), + nullptr + ); + EXPECT_EQ( + m_childOfChild->m_entity->FindComponent()->GetHierarchicalRoot(), + nullptr + ); + } + + TEST_F(ServerBranchedHierarchyTests, Detach_Child_Then_Attach_To_Child2) + { + m_child->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_child->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + /* + * Sets up 2 deep hierarchies. + */ + class ServerHierarchyOfHierarchyTests : public ServerDeepHierarchyTests + { + public: + const NetEntityId Root2NetEntityId = NetEntityId{ 4 }; + const NetEntityId Child2NetEntityId = NetEntityId{ 5 }; + const NetEntityId ChildOfChild2NetEntityId = NetEntityId{ 6 }; + + void SetUp() override + { + ServerDeepHierarchyTests::SetUp(); + + m_root2 = AZStd::make_unique((4), "root 2", Root2NetEntityId, EntityInfo::Role::Root); + m_child2 = AZStd::make_unique((5), "child 2", Child2NetEntityId, EntityInfo::Role::Child); + m_childOfChild2 = AZStd::make_unique((6), "child of child 2", ChildOfChild2NetEntityId, EntityInfo::Role::Child); + + CreateDeepHierarchy(*m_root2, *m_child2, *m_childOfChild2); + + m_child2->m_entity->FindComponent()->SetParent(m_root2->m_entity->GetId()); + m_childOfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChild2.reset(); + m_child2.reset(); + m_root2.reset(); + + ServerDeepHierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_root2; + AZStd::unique_ptr m_child2; + AZStd::unique_ptr m_childOfChild2; + }; + + TEST_F(ServerHierarchyOfHierarchyTests, Hierarchies_Are_Not_Related) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + + if (m_root->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child->m_entity.get() + ); + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild->m_entity.get() + ); + } + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + + if (m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root2->m_entity.get() + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child2->m_entity.get() + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild2->m_entity.get() + ); + } + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Is_Not_Top_Level_Root) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->IsHierarchicalChild(), + false + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), + true + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_References_All_When_Another_Hierarchy_Attached_At_Root) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_References_All_When_Another_Hierarchy_Attached_At_Child) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_References_All_When_Another_Hierarchy_Attached_At_Child_Of_Child) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_References_Top_Root_When_Another_Hierarchy_Attached_At_Root) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), + true + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_References_Top_Root_When_Another_Hierarchy_Attached_At_Child) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), + true + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_References_Top_Root_When_Another_Hierarchy_Attached_At_Child_Of_Child) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), + true + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchyRoot(), + RootNetEntityId + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Doesnt_Keep_Child_References) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 0 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Detachment_From_Top_Root) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + // detach + m_root2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + if (m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size() == 3) + { + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[0], + m_root2->m_entity.get() + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[1], + m_child2->m_entity.get() + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities()[2], + m_childOfChild2->m_entity.get() + ); + } + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Detachment_From_Child_Of_Child) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + // detach + m_root2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Top_Root_Deactivates) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_root.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Child_Of_Top_Root_Deactivates) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_child.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_Child_References_After_Child_Of_Child_Deactivates) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_childOfChild.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Stress_Test_Inner_Root_Has_Child_References_After_Detachment_From_Child_Of_Child) + { + for (int i = 0; i < 100; ++i) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + // detach + m_root2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + } + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Detachment_Of_Child_Of_Child_In_Inner_Hierarchy) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + // detach + m_childOfChild2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 5 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Attachment_Of_Child_Of_Child_In_Inner_Hierarchy) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + // detach + m_childOfChild2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + // re-connect + m_childOfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Child_Of_Child_Changed_Hierarchies) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + // detach + m_childOfChild2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + // connect to a different hierarchy + m_childOfChild2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Detachment_Of_Child_In_Inner_Hierarchy) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + // detach + m_child2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 4 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Top_Root_Updates_Child_References_After_Child_Changed_Hierarchies) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + // detach + m_child2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + // connect to a different hierarchy + m_child2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 6 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Has_No_Child_References_After_All_Children_Moved_To_Another_Hierarchy) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_child2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + // detach + m_root2->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Inner_Root_Child_Deactivated_Top_Root_Has_No_Child_Reference_To_It) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_child2.reset(); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 4 + ); + } + + TEST_F(ServerHierarchyOfHierarchyTests, Testing_Limiting_Hierarchy_Maximum_Size) + { + uint32_t currentMaxLimit = 0; + m_console->GetCvarValue("bg_hierarchyEntityMaxLimit", currentMaxLimit); + m_console->PerformCommand("bg_hierarchyEntityMaxLimit 2"); + + // remake the top level hierarchy + m_root->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_root->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + + m_console->PerformCommand((AZStd::string("bg_hierarchyEntityMaxLimit ") + AZStd::to_string(currentMaxLimit)).c_str()); + m_console->GetCvarValue("bg_hierarchyEntityMaxLimit", currentMaxLimit); + } + + /* + * Parent -> Child -> ChildOfChild (not marked as in a hierarchy) + */ + class ServerMixedDeepHierarchyTests : public HierarchyTests + { + public: + void SetUp() override + { + HierarchyTests::SetUp(); + + m_root = AZStd::make_unique((1), "root", NetEntityId{ 1 }, EntityInfo::Role::Root); + m_child = AZStd::make_unique((2), "child", NetEntityId{ 2 }, EntityInfo::Role::Child); + m_childOfChild = AZStd::make_unique((3), "child of child", NetEntityId{ 3 }, EntityInfo::Role::None); + + CreateDeepHierarchy(*m_root, *m_child, *m_childOfChild); + + m_child->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_child->m_entity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChild.reset(); + m_child.reset(); + m_root.reset(); + + HierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_root; + AZStd::unique_ptr m_child; + AZStd::unique_ptr m_childOfChild; + }; + + TEST_F(ServerMixedDeepHierarchyTests, Top_Root_Ignores_Non_Hierarchical_Entities) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerMixedDeepHierarchyTests, Detaching_Non_Hierarchical_Entity_Has_No_Effect_On_Top_Root) + { + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + TEST_F(ServerMixedDeepHierarchyTests, Attaching_Non_Hierarchical_Entity_Has_No_Effect_On_Top_Root) + { + m_childOfChild->m_entity->FindComponent()->SetParent(AZ::EntityId()); + m_childOfChild->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + } + + /* + * 1st hierarchy: Parent -> Child -> ChildOfChild (not marked as in a hierarchy) + * 2nd hierarchy: Parent2 -> Child2 (not marked as in a hierarchy) -> ChildOfChild2 + */ + class ServerMixedHierarchyOfHierarchyTests : public ServerMixedDeepHierarchyTests + { + public: + void SetUp() override + { + ServerMixedDeepHierarchyTests::SetUp(); + + m_root2 = AZStd::make_unique((4), "root 2", NetEntityId{ 4 }, EntityInfo::Role::Root); + m_child2 = AZStd::make_unique((5), "child 2", NetEntityId{ 5 }, EntityInfo::Role::None); + m_childOfChild2 = AZStd::make_unique((6), "child of child 2", NetEntityId{ 6 }, EntityInfo::Role::Child); + + CreateDeepHierarchy(*m_root2, *m_child2, *m_childOfChild2); + + m_child2->m_entity->FindComponent()->SetParent(m_root2->m_entity->GetId()); + m_childOfChild2->m_entity->FindComponent()->SetParent(m_child2->m_entity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChild2.reset(); + m_child2.reset(); + m_root2.reset(); + + ServerMixedDeepHierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_root2; + AZStd::unique_ptr m_child2; + AZStd::unique_ptr m_childOfChild2; + }; + + TEST_F(ServerMixedHierarchyOfHierarchyTests, Sanity_Check_Ingore_Children_Without_Hierarchy_Components) + { + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 2 + ); + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 1 + ); + } + + TEST_F(ServerMixedHierarchyOfHierarchyTests, Adding_Mixed_Hierarchy_Ingores_Children_Without_Hierarchy_Components) + { + m_root2->m_entity->FindComponent()->SetParent(m_root->m_entity->GetId()); + + EXPECT_EQ( + m_root->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerMixedHierarchyOfHierarchyTests, Attaching_Hierarchy_To_Non_Hierarchical_Entity_Does_Not_Merge_Hierarchies) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->IsHierarchicalChild(), + false + ); + } + + /* + * Sets up a hierarchy with 3 roots, 2 of them being inner roots. + */ + class ServerHierarchyWithThreeRoots : public ServerHierarchyOfHierarchyTests + { + public: + const NetEntityId Root3NetEntityId = NetEntityId{ 7 }; + const NetEntityId Child3NetEntityId = NetEntityId{ 8 }; + const NetEntityId ChildOfChild3NetEntityId = NetEntityId{ 9 }; + + void SetUp() override + { + ServerHierarchyOfHierarchyTests::SetUp(); + + m_root3 = AZStd::make_unique((7), "root 3", Root3NetEntityId, EntityInfo::Role::Root); + m_child3 = AZStd::make_unique((8), "child 3", Child3NetEntityId, EntityInfo::Role::Child); + m_childOfChild3 = AZStd::make_unique((9), "child of child 3", ChildOfChild3NetEntityId, EntityInfo::Role::Child); + + CreateDeepHierarchy(*m_root3, *m_child3, *m_childOfChild3); + + m_child3->m_entity->FindComponent()->SetParent(m_root3->m_entity->GetId()); + m_childOfChild3->m_entity->FindComponent()->SetParent(m_child3->m_entity->GetId()); + // now the entities are under one hierarchy + } + + void TearDown() override + { + m_childOfChild3.reset(); + m_child3.reset(); + m_root3.reset(); + + ServerHierarchyOfHierarchyTests::TearDown(); + } + + AZStd::unique_ptr m_root3; + AZStd::unique_ptr m_child3; + AZStd::unique_ptr m_childOfChild3; + }; + + TEST_F(ServerHierarchyWithThreeRoots, Top_Root_Active_Then_Inner_Roots_Have_No_Child_References) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_root3->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 0 + ); + EXPECT_EQ( + m_root3->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 0 + ); + } + + TEST_F(ServerHierarchyWithThreeRoots, Top_Root_Deactivates_Inner_Roots_Have_Child_References) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_root3->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_root.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + EXPECT_EQ( + m_root3->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyWithThreeRoots, Child_Of_Top_Root_Deactivates_Inner_Roots_Have_Child_References) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_root3->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_child.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + EXPECT_EQ( + m_root3->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } + + TEST_F(ServerHierarchyWithThreeRoots, Child_Of_Child_Of_Top_Root_Deactivates_Inner_Roots_Have_Child_References) + { + m_root2->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + m_root3->m_entity->FindComponent()->SetParent(m_childOfChild->m_entity->GetId()); + + m_childOfChild.reset(); + + EXPECT_EQ( + m_root2->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + EXPECT_EQ( + m_root3->m_entity->FindComponent()->GetHierarchicalEntities().size(), + 3 + ); + } +} diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index 54311a0abc..3516255c7d 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -15,23 +15,31 @@ set(FILES Include/Multiplayer/MultiplayerTypes.h Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h Include/Multiplayer/Components/MultiplayerComponent.h - Include/Multiplayer/Components/MultiplayerController.h Include/Multiplayer/Components/MultiplayerComponentRegistry.h + Include/Multiplayer/Components/MultiplayerController.h Include/Multiplayer/Components/NetBindComponent.h + Include/Multiplayer/Components/NetworkHierarchyChildComponent.h + Include/Multiplayer/Components/NetworkHierarchyRootComponent.h + Include/Multiplayer/Components/NetworkHierarchyBus.h Include/Multiplayer/Components/NetworkCharacterComponent.h Include/Multiplayer/Components/NetworkHitVolumesComponent.h Include/Multiplayer/Components/NetworkRigidBodyComponent.h Include/Multiplayer/Components/NetworkTransformComponent.h Include/Multiplayer/ConnectionData/IConnectionData.h Include/Multiplayer/EntityDomains/IEntityDomain.h - Include/Multiplayer/NetworkEntity/INetworkEntityManager.h + Include/Multiplayer/IMultiplayer.h + Include/Multiplayer/IMultiplayerTools.h Include/Multiplayer/INetworkSpawnableLibrary.h + Include/Multiplayer/MultiplayerConstants.h + Include/Multiplayer/MultiplayerStats.h + Include/Multiplayer/MultiplayerTypes.h + Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h Include/Multiplayer/NetworkEntity/IFilterEntityManager.h - Include/Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h - Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h + Include/Multiplayer/NetworkEntity/INetworkEntityManager.h Include/Multiplayer/NetworkEntity/NetworkEntityHandle.h Include/Multiplayer/NetworkEntity/NetworkEntityHandle.inl - Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h + Include/Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h + Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h Include/Multiplayer/NetworkInput/IMultiplayerComponentInput.h Include/Multiplayer/NetworkInput/NetworkInput.h Include/Multiplayer/NetworkTime/INetworkTime.h @@ -43,14 +51,11 @@ set(FILES Include/Multiplayer/NetworkTime/RewindableObject.inl Include/Multiplayer/Physics/PhysicsUtils.h Include/Multiplayer/ReplicationWindows/IReplicationWindow.h - Source/MultiplayerSystemComponent.cpp - Source/MultiplayerSystemComponent.h - Source/MultiplayerStats.cpp - Source/AutoGen/AutoComponent_Header.jinja - Source/AutoGen/AutoComponent_Source.jinja - Source/AutoGen/AutoComponent_Common.jinja Source/AutoGen/AutoComponentTypes_Header.jinja Source/AutoGen/AutoComponentTypes_Source.jinja + Source/AutoGen/AutoComponent_Common.jinja + Source/AutoGen/AutoComponent_Header.jinja + Source/AutoGen/AutoComponent_Source.jinja Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml Source/AutoGen/Multiplayer.AutoPackets.xml Source/AutoGen/MultiplayerEditor.AutoPackets.xml @@ -58,11 +63,15 @@ set(FILES Source/AutoGen/NetworkHitVolumesComponent.AutoComponent.xml Source/AutoGen/NetworkRigidBodyComponent.AutoComponent.xml Source/AutoGen/NetworkTransformComponent.AutoComponent.xml + Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.xml + Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml Source/Components/LocalPredictionPlayerInputComponent.cpp Source/Components/MultiplayerComponent.cpp - Source/Components/MultiplayerController.cpp Source/Components/MultiplayerComponentRegistry.cpp + Source/Components/MultiplayerController.cpp Source/Components/NetBindComponent.cpp + Source/Components/NetworkHierarchyChildComponent.cpp + Source/Components/NetworkHierarchyRootComponent.cpp Source/Components/NetworkCharacterComponent.cpp Source/Components/NetworkHitVolumesComponent.cpp Source/Components/NetworkRigidBodyComponent.cpp @@ -77,6 +86,9 @@ set(FILES Source/Editor/MultiplayerEditorConnection.h Source/EntityDomains/FullOwnershipEntityDomain.cpp Source/EntityDomains/FullOwnershipEntityDomain.h + Source/MultiplayerStats.cpp + Source/MultiplayerSystemComponent.cpp + Source/MultiplayerSystemComponent.h Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp Source/NetworkEntity/EntityReplication/EntityReplicationManager.h Source/NetworkEntity/EntityReplication/EntityReplicator.cpp @@ -92,13 +104,13 @@ set(FILES Source/NetworkEntity/NetworkEntityHandle.cpp Source/NetworkEntity/NetworkEntityManager.cpp Source/NetworkEntity/NetworkEntityManager.h - Source/NetworkEntity/NetworkSpawnableLibrary.cpp - Source/NetworkEntity/NetworkSpawnableLibrary.h Source/NetworkEntity/NetworkEntityRpcMessage.cpp Source/NetworkEntity/NetworkEntityTracker.cpp Source/NetworkEntity/NetworkEntityTracker.h Source/NetworkEntity/NetworkEntityTracker.inl Source/NetworkEntity/NetworkEntityUpdateMessage.cpp + Source/NetworkEntity/NetworkSpawnableLibrary.cpp + Source/NetworkEntity/NetworkSpawnableLibrary.h Source/NetworkInput/NetworkInput.cpp Source/NetworkInput/NetworkInputArray.cpp Source/NetworkInput/NetworkInputArray.h @@ -110,11 +122,8 @@ set(FILES Source/NetworkInput/NetworkInputMigrationVector.h Source/NetworkTime/NetworkTime.cpp Source/NetworkTime/NetworkTime.h - Source/Pipeline/NetBindMarkerComponent.cpp - Source/Pipeline/NetBindMarkerComponent.h Source/Pipeline/NetworkSpawnableHolderComponent.cpp Source/Pipeline/NetworkSpawnableHolderComponent.h - Source/Physics/PhysicsUtils.cpp Source/ReplicationWindows/NullReplicationWindow.cpp Source/ReplicationWindows/NullReplicationWindow.h Source/ReplicationWindows/ServerToClientReplicationWindow.cpp diff --git a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake index f385a21600..3f4fcc9efa 100644 --- a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake @@ -8,6 +8,10 @@ set(FILES Tests/Main.cpp + Tests/MockInterfaces.h + Tests/ClientHierarchyTests.cpp + Tests/ServerHierarchyTests.cpp + Tests/CommonHierarchySetup.h Tests/IMultiplayerConnectionMock.h Tests/MultiplayerSystemTests.cpp Tests/RewindableContainerTests.cpp diff --git a/Gems/Multiplayer/gem.json b/Gems/Multiplayer/gem.json index 8abbddea5f..47dbddfcbd 100644 --- a/Gems/Multiplayer/gem.json +++ b/Gems/Multiplayer/gem.json @@ -5,8 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Multiplayer Gem provides a public API for multiplayer functionality such as connecting and hosting.", - "canonical_tags": ["Gem"], - "user_tags": ["Multiplayer", "Network", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Multiplayer", + "Network", + "Framework" + ], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "dependencies": [ + "CertificateManager", + "Atom_Feature_Common", + "ImGui" + ] } diff --git a/Gems/MultiplayerCompression/gem.json b/Gems/MultiplayerCompression/gem.json index 178cca0ffe..7dd31476e3 100644 --- a/Gems/MultiplayerCompression/gem.json +++ b/Gems/MultiplayerCompression/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Multiplayer Compression Gem provides an open source Compressor for use with AzNetworking's transport layer.", - "canonical_tags": ["Gem"], - "user_tags": ["Multiplayer", "Network", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Multiplayer", + "Network", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/multiplayer/multiplayer-compression/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/multiplayer/multiplayer-compression/", + "dependencies": [] } diff --git a/Gems/NvCloth/gem.json b/Gems/NvCloth/gem.json index 49ccbf32f1..019ce23742 100644 --- a/Gems/NvCloth/gem.json +++ b/Gems/NvCloth/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The NVIDIA Cloth Gem provides functionality to create fast, realistic cloth simulation with the NVIDIA Cloth library.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/nvidia-cloth/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/nvidia-cloth/", + "dependencies": [ + "CommonFeaturesAtom", + "EMotionFX" + ] } diff --git a/Gems/PBSreferenceMaterials/gem.json b/Gems/PBSreferenceMaterials/gem.json index 563dd31a38..feddc1e465 100644 --- a/Gems/PBSreferenceMaterials/gem.json +++ b/Gems/PBSreferenceMaterials/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The PBS Reference Materials Gem provides physically based reference materials for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Sample", "Assets"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Sample", + "Assets" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/pbs-reference-materials/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/pbs-reference-materials/", + "dependencies": [] } diff --git a/Gems/PhysX/gem.json b/Gems/PhysX/gem.json index 0fbd3e44f9..bacbcf2dee 100644 --- a/Gems/PhysX/gem.json +++ b/Gems/PhysX/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The PhysX Gem provides physics simulation with NVIDIA PhysX including static and dynamic rigid body simulation, force regions, ragdolls, and dynamic PhysX joints.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "SDK"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "SDK" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx/", + "dependencies": [ + "LmbrCentral", + "CommonFeaturesAtom" + ] } diff --git a/Gems/PhysXDebug/gem.json b/Gems/PhysXDebug/gem.json index f7877cabdd..ece0774210 100644 --- a/Gems/PhysXDebug/gem.json +++ b/Gems/PhysXDebug/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The PhysX Debug Gem provides debugging functionality and visualizations for NVIDIA PhysX in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "Debug"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "Debug" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx-debug/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx-debug/", + "dependencies": [ + "PhysX", + "ImGui" + ] } diff --git a/Gems/PhysXSamples/gem.json b/Gems/PhysXSamples/gem.json index e48dc6991b..0c84a29f47 100644 --- a/Gems/PhysXSamples/gem.json +++ b/Gems/PhysXSamples/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The PhysX Samples Gem provides sample assets and scripts that demonstrate PhysX Gem features in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Physics", "Simulation", "Sample"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Physics", + "Simulation", + "Sample" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx-samples/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/physics/nvidia/physx-samples/", + "dependencies": [] } diff --git a/Gems/Prefab/PrefabBuilder/gem.json b/Gems/Prefab/PrefabBuilder/gem.json index ad6062ae3b..ba78f96358 100644 --- a/Gems/Prefab/PrefabBuilder/gem.json +++ b/Gems/Prefab/PrefabBuilder/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Prefab Builder Gem provides an Asset Processor module for prefabs, which are complex assets built by combining smaller entities.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Utility", "Core"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Utility", + "Core" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/prefab/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/prefab/", + "dependencies": [] } diff --git a/Gems/Presence/gem.json b/Gems/Presence/gem.json index 49d937946d..a70953ae4e 100644 --- a/Gems/Presence/gem.json +++ b/Gems/Presence/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Presence Gem provides a target platform agnostic interface for Presence services.", - "canonical_tags": ["Gem"], - "user_tags": ["Network", "Gameplay", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Network", + "Gameplay", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/presence/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/presence/", + "dependencies": [] } diff --git a/Gems/PrimitiveAssets/gem.json b/Gems/PrimitiveAssets/gem.json index d1789d46fe..4ad3cb62ad 100644 --- a/Gems/PrimitiveAssets/gem.json +++ b/Gems/PrimitiveAssets/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Primitive Assets Gem provides primitive shape mesh assets with physics enabled.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Sample", "Debug"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Sample", + "Debug" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/primitive-assets/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/primitive-assets/", + "dependencies": [] } diff --git a/Gems/PythonAssetBuilder/gem.json b/Gems/PythonAssetBuilder/gem.json index 1a76d7d2c2..ce30ba9e82 100644 --- a/Gems/PythonAssetBuilder/gem.json +++ b/Gems/PythonAssetBuilder/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Python Asset Builder Gem provides functionality to implement custom asset builders in Python for Asset Processor.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Assets", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Assets", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/python-asset-builder/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/python-asset-builder/", + "dependencies": [ + "EditorPythonBindings" + ] } diff --git a/Gems/QtForPython/gem.json b/Gems/QtForPython/gem.json index 1519d46c52..f83be43342 100644 --- a/Gems/QtForPython/gem.json +++ b/Gems/QtForPython/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Qt for Python Gem provides the PySide2 Python libraries to manage Qt widgets.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "UI", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "UI", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/qt-for-python/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/python/qt-for-python/", + "dependencies": [ + "EditorPythonBindings" + ] } diff --git a/Gems/SaveData/gem.json b/Gems/SaveData/gem.json index 3892cc80c2..333b4682d2 100644 --- a/Gems/SaveData/gem.json +++ b/Gems/SaveData/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Save Data Gem provides a platform independent API to save and load persistent user data in Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["Utility", "Gameplay"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Utility", + "Gameplay" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/save-data/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/save-data/", + "dependencies": [] } diff --git a/Gems/SceneLoggingExample/gem.json b/Gems/SceneLoggingExample/gem.json index ff7def1b32..16961b9c5b 100644 --- a/Gems/SceneLoggingExample/gem.json +++ b/Gems/SceneLoggingExample/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Scene Logging Example Gem demonstrates the basics of extending the Open 3D Engine Scene API by adding additional logging to the pipeline.", - "canonical_tags": ["Gem"], - "user_tags": ["Debug", "Sample"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Debug", + "Sample" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/scene-logging-example/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/scene-logging-example/", + "dependencies": [] } diff --git a/Gems/SceneProcessing/gem.json b/Gems/SceneProcessing/gem.json index a6c2cefbd6..de1dfeb23f 100644 --- a/Gems/SceneProcessing/gem.json +++ b/Gems/SceneProcessing/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Scene Processing Gem provides Scene Settings, a tool you can use to specify the default settings for processing asset files for actors, meshes, motions, and PhysX.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Tools", "Core"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Tools", + "Core" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/scene-processing/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/scene-processing/", + "dependencies": [] } diff --git a/Gems/ScriptCanvas/gem.json b/Gems/ScriptCanvas/gem.json index b6b5522d7d..621ea42961 100644 --- a/Gems/ScriptCanvas/gem.json +++ b/Gems/ScriptCanvas/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Gem provides Open 3D Engine's visual scripting environment, Script Canvas.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas/", + "dependencies": [ + "ScriptEvents", + "ExpressionEvaluation", + "GraphCanvas" + ] } diff --git a/Gems/ScriptCanvasDeveloper/gem.json b/Gems/ScriptCanvasDeveloper/gem.json index d7b0aefad3..ee1bfcec84 100644 --- a/Gems/ScriptCanvasDeveloper/gem.json +++ b/Gems/ScriptCanvasDeveloper/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Developer Gem provides a suite of utility features for the development and debugging of Script Canvas systems.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Utility", "Debug"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Utility", + "Debug" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-developer/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-developer/", + "dependencies": [ + "ScriptCanvas", + "GraphCanvas" + ] } diff --git a/Gems/ScriptCanvasPhysics/gem.json b/Gems/ScriptCanvasPhysics/gem.json index 46e2ef1194..417fa6893a 100644 --- a/Gems/ScriptCanvasPhysics/gem.json +++ b/Gems/ScriptCanvasPhysics/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Script Canvas Physics Gem provides Script Canvas nodes for physics scene queries such as raycasts.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Physics", "Simulation"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Physics", + "Simulation" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-physics/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-physics/", + "dependencies": [ + "ScriptCanvas" + ] } diff --git a/Gems/ScriptCanvasTesting/gem.json b/Gems/ScriptCanvasTesting/gem.json index 303eab8c7e..c45d2ac165 100644 --- a/Gems/ScriptCanvasTesting/gem.json +++ b/Gems/ScriptCanvasTesting/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Testing Gem provides a framework for testing for and with Script Canvas.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Debug", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Debug", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-testing/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-canvas-testing/", + "dependencies": [ + "ScriptCanvas", + "GraphCanvas", + "ScriptEvents" + ] } diff --git a/Gems/ScriptEvents/gem.json b/Gems/ScriptEvents/gem.json index 2b7d74ef3f..386d9b5614 100644 --- a/Gems/ScriptEvents/gem.json +++ b/Gems/ScriptEvents/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Script Events Gem provides a framework for creating event assets usable from any scripting solution in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "Framework", "Gameplay"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "Framework", + "Gameplay" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-events/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/script-events/", + "dependencies": [] } diff --git a/Gems/ScriptedEntityTweener/gem.json b/Gems/ScriptedEntityTweener/gem.json index a0f558356b..c51f05df9a 100644 --- a/Gems/ScriptedEntityTweener/gem.json +++ b/Gems/ScriptedEntityTweener/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Scripted Entity Tweener Gem provides a script driven animation system for Open 3D Engine projects.", - "canonical_tags": ["Gem"], - "user_tags": ["Scripting", "UI", "Animation"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Scripting", + "UI", + "Animation" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/scripted-entity-tweener/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/script/scripted-entity-tweener/", + "dependencies": [] } diff --git a/Gems/SliceFavorites/gem.json b/Gems/SliceFavorites/gem.json index 3ae37e2145..84f42fc1a2 100644 --- a/Gems/SliceFavorites/gem.json +++ b/Gems/SliceFavorites/gem.json @@ -5,7 +5,11 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "Add the ability to favorite a slice to allow easy access and instantiation", - "user_tags": ["Editor", "Slices"], + "user_tags": [ + "Editor", + "Slices" + ], "icon_path": "preview.png", - "requirements": "" + "requirements": "", + "dependencies": [] } diff --git a/Gems/StartingPointCamera/gem.json b/Gems/StartingPointCamera/gem.json index 5a9aa0df9b..613eb76267 100644 --- a/Gems/StartingPointCamera/gem.json +++ b/Gems/StartingPointCamera/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Camera Gem provides the behaviors used with the Camera Framework Gem to define a camera rig.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/starting-point-camera/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/starting-point-camera/", + "dependencies": [ + "CameraFramework", + "LmbrCentral" + ] } diff --git a/Gems/StartingPointInput/gem.json b/Gems/StartingPointInput/gem.json index e65f271fe5..d2641ea27b 100644 --- a/Gems/StartingPointInput/gem.json +++ b/Gems/StartingPointInput/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Input Gem provides functionality to map low-level input events to high-level actions.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/starting-point-input/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/starting-point-input/", + "dependencies": [ + "ScriptCanvas" + ] } diff --git a/Gems/StartingPointMovement/gem.json b/Gems/StartingPointMovement/gem.json index 70e1d105c1..7def6da768 100644 --- a/Gems/StartingPointMovement/gem.json +++ b/Gems/StartingPointMovement/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Movement Gem provides a series of Lua scripts that listen and respond to input events and trigger transform operations such as translation and rotation.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay", "Scripting"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay", + "Scripting" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/starting-point-movement/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/starting-point-movement/", + "dependencies": [] } diff --git a/Gems/SurfaceData/gem.json b/Gems/SurfaceData/gem.json index 57e4d8fb70..51a134d5df 100644 --- a/Gems/SurfaceData/gem.json +++ b/Gems/SurfaceData/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Surface Data Gem provides functionality to emit signals or tags from surfaces such as meshes and terrain.", - "canonical_tags": ["Gem"], - "user_tags": ["Environment", "Utility", "Design"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Environment", + "Utility", + "Design" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/surface-data/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/surface-data/", + "dependencies": [ + "LmbrCentral", + "Atom_RPI", + "Atom_Feature_Common" + ] } diff --git a/Gems/Terrain/gem.json b/Gems/Terrain/gem.json index c01ea7e534..cd72a91708 100644 --- a/Gems/Terrain/gem.json +++ b/Gems/Terrain/gem.json @@ -4,7 +4,21 @@ "license": "Apache-2.0 Or MIT", "origin": "Open 3D Engine - o3de.org", "summary": "The Terrain Gem is an experimental terrain system. The terrain system maps height, color, and surface data to regions of the world, provides gradient-based and shape-based authoring tools and workflows, includes specialized rendering for efficient display, and integrates with physics for physical simulation.", - "canonical_tags": [ "Gem" ], - "user_tags": [ "Environment", "Tools", "Design", "Terrain" ], - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/terrain/" + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Environment", + "Tools", + "Design", + "Terrain" + ], + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/terrain/", + "dependencies": [ + "Atom_RPI", + "Atom", + "GradientSignal", + "SurfaceData", + "LmbrCentral" + ] } diff --git a/Gems/TestAssetBuilder/gem.json b/Gems/TestAssetBuilder/gem.json index 1e0b84894a..ba7568005f 100644 --- a/Gems/TestAssetBuilder/gem.json +++ b/Gems/TestAssetBuilder/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Test Asset Builder Gem is used to feature test Asset Processor.", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "Debug", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "Debug", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/test-asset-builder/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/assets/test-asset-builder/", + "dependencies": [] } diff --git a/Gems/TextureAtlas/gem.json b/Gems/TextureAtlas/gem.json index 832dee0e62..345e085359 100644 --- a/Gems/TextureAtlas/gem.json +++ b/Gems/TextureAtlas/gem.json @@ -5,9 +5,19 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Texture Atlas Gem provides the formatting for texture atlases from 2D textures for LyShine.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Assets", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Assets", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/texture-atlas/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/utility/texture-atlas/", + "dependencies": [ + "Atom_RPI", + "ImageProcessingAtom" + ] } diff --git a/Gems/TickBusOrderViewer/gem.json b/Gems/TickBusOrderViewer/gem.json index 5ea936960a..dc5cb6f66c 100644 --- a/Gems/TickBusOrderViewer/gem.json +++ b/Gems/TickBusOrderViewer/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Tick Bus Order Viewer Gem provides a console variable that displays the order of runtime tick events.", - "canonical_tags": ["Gem"], - "user_tags": ["Gameplay", "Simulation", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Gameplay", + "Simulation", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/tick-bus-order-viewer/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/gameplay/tick-bus-order-viewer/", + "dependencies": [] } diff --git a/Gems/Twitch/gem.json b/Gems/Twitch/gem.json index 6f875e9171..f45433bc3f 100644 --- a/Gems/Twitch/gem.json +++ b/Gems/Twitch/gem.json @@ -5,9 +5,18 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Twitch Gem provides access to the Twitch API v5 SDK including social functions, channels, and other APIs.", - "canonical_tags": ["Gem"], - "user_tags": ["Network", "SDK", "Multiplayer"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Network", + "SDK", + "Multiplayer" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/twitch/twitch/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/network/twitch/twitch/", + "dependencies": [ + "HttpRequestor" + ] } diff --git a/Gems/UiBasics/gem.json b/Gems/UiBasics/gem.json index d1e06eb15a..9a1e16a462 100644 --- a/Gems/UiBasics/gem.json +++ b/Gems/UiBasics/gem.json @@ -5,9 +5,16 @@ "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The UI Basics Gem provides a collection of basic UI prefabs such as image, text, and button, that can be used with LyShine, the Open 3D Engine runtime User Interface system and editor.", - "canonical_tags": ["Gem"], - "user_tags": ["UI", "Assets", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "UI", + "Assets", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/ui-basics/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/ui/ui-basics/", + "dependencies": [] } diff --git a/Gems/Vegetation/gem.json b/Gems/Vegetation/gem.json index 3119d67560..9dbcc3450b 100644 --- a/Gems/Vegetation/gem.json +++ b/Gems/Vegetation/gem.json @@ -5,9 +5,21 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Vegetation Gem provides tools to place natural-looking vegetation in Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Environment", "Tools", "Design"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Environment", + "Tools", + "Design" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/vegetation/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/environment/vegetation/", + "dependencies": [ + "LmbrCentral", + "SurfaceData", + "CommonFeaturesAtom", + "GradientSignal" + ] } diff --git a/Gems/VideoPlaybackFramework/gem.json b/Gems/VideoPlaybackFramework/gem.json index 15b200c8db..9f491f47cb 100644 --- a/Gems/VideoPlaybackFramework/gem.json +++ b/Gems/VideoPlaybackFramework/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Video Playback Framework Gem provides the interface to play back video.", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Framework"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Framework" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/video-playback-framework/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/video-playback-framework/", + "dependencies": [] } diff --git a/Gems/VirtualGamepad/gem.json b/Gems/VirtualGamepad/gem.json index 472e8b93fe..c6305f1754 100644 --- a/Gems/VirtualGamepad/gem.json +++ b/Gems/VirtualGamepad/gem.json @@ -5,9 +5,15 @@ "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Virtual Gamepad Gem provides controls that emulate a gamepad on touch screen devices.", - "canonical_tags": ["Gem"], - "user_tags": ["Input", "Gameplay"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Input", + "Gameplay" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/virtual-gamepad/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/input/virtual-gamepad/", + "dependencies": [] } diff --git a/Gems/WhiteBox/gem.json b/Gems/WhiteBox/gem.json index 0e44e2fe5e..81e0ad5f88 100644 --- a/Gems/WhiteBox/gem.json +++ b/Gems/WhiteBox/gem.json @@ -5,9 +5,20 @@ "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The White Box Gem provides White Box rapid design components for Open 3D Engine.", - "canonical_tags": ["Gem"], - "user_tags": ["Design", "Tools", "Utility"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Design", + "Tools", + "Utility" + ], "icon_path": "preview.png", "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/design/white-box/" + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/design/white-box/", + "dependencies": [ + "Atom_RPI", + "Atom_Feature_Common", + "CommonFeaturesAtom" + ] } diff --git a/cmake/3rdParty/FindX11.cmake b/cmake/3rdParty/FindX11.cmake new file mode 100644 index 0000000000..98cc57e40e --- /dev/null +++ b/cmake/3rdParty/FindX11.cmake @@ -0,0 +1,29 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +# Open a new scope so we can make changes to CMAKE_MODULE_PATH, and restore it +# when we're done +function(FindX11) + # O3DE's FindX11.cmake is a wrapper for the one that CMake provides. Remove + # our current directory from CMAKE_MODULE_PATH to avoid recursive includes + list(REMOVE_ITEM CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + + find_package(X11 COMPONENTS ${X11_FIND_COMPONENTS} QUIET) + + foreach(component IN LISTS X11_FIND_COMPONENTS) + ly_add_external_target( + SYSTEM + PACKAGE X11 + NAME ${component} + VERSION "" + BUILD_DEPENDENCIES + X11::${component} + ) + endforeach() +endfunction() +FindX11() diff --git a/cmake/LYWrappers.cmake b/cmake/LYWrappers.cmake index 8e5633b5e6..d388b03b75 100644 --- a/cmake/LYWrappers.cmake +++ b/cmake/LYWrappers.cmake @@ -477,11 +477,23 @@ function(ly_parse_third_party_dependencies ly_THIRD_PARTY_LIBRARIES) if(${dependency_namespace} STREQUAL "3rdParty") if (NOT TARGET ${dependency}) list(GET dependency_list 1 dependency_package) + list(LENGTH dependency_list dependency_list_length) ly_download_associated_package(${dependency_package}) - find_package(${dependency_package} REQUIRED MODULE) + if (dependency_list_length GREATER 2) + # There's an optional interface specified + list(GET dependency_list 2 component) + list(APPEND packages_with_components ${dependency_package}) + list(APPEND ${dependency_package}_components ${component}) + else() + find_package(${dependency_package} REQUIRED MODULE) + endif() endif() endif() endforeach() + + foreach(dependency IN LISTS packages_with_components) + find_package(${dependency} REQUIRED MODULE COMPONENTS ${${dependency}_components}) + endforeach() endfunction() #! ly_configure_target_platform_properties: Configures any platform specific properties on target diff --git a/cmake/Platform/Mac/PAL_mac.cmake b/cmake/Platform/Mac/PAL_mac.cmake index 065b35d69c..561ce86570 100644 --- a/cmake/Platform/Mac/PAL_mac.cmake +++ b/cmake/Platform/Mac/PAL_mac.cmake @@ -37,6 +37,10 @@ endif() # Set the default asset type for deployment set(LY_ASSET_DEPLOY_ASSET_TYPE "mac" CACHE STRING "Set the asset type for deployment.") +# Set the deployment target for MacOS +set(LY_MAC_DEPLOYMENT_TARGET "11.0" CACHE STRING "Mac Deployment Target") +set(CMAKE_OSX_DEPLOYMENT_TARGET ${LY_MAC_DEPLOYMENT_TARGET}) + # Set the python cmd tool ly_set(LY_PYTHON_CMD ${CMAKE_CURRENT_SOURCE_DIR}/python/python.sh) diff --git a/cmake/Platform/iOS/Toolchain_ios.cmake b/cmake/Platform/iOS/Toolchain_ios.cmake index 49f0b23461..719c492ebb 100644 --- a/cmake/Platform/iOS/Toolchain_ios.cmake +++ b/cmake/Platform/iOS/Toolchain_ios.cmake @@ -13,7 +13,7 @@ set(CMAKE_OSX_ARCHITECTURES arm64) set(LY_IOS_CODE_SIGNING_IDENTITY "iPhone Developer" CACHE STRING "iPhone Developer") -set(LY_IOS_DEPLOYMENT_TARGET "13.0" CACHE STRING "iOS Deployment Target") +set(LY_IOS_DEPLOYMENT_TARGET "14.0" CACHE STRING "iOS Deployment Target") set(LY_IOS_DEVELOPMENT_TEAM "CF9TGN983S" CACHE STRING "The development team ID") diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 5bc4b919fb..5fe19ddf46 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -9,6 +9,7 @@ import groovy.json.JsonOutput PIPELINE_CONFIG_FILE = 'scripts/build/Jenkins/lumberyard.json' INCREMENTAL_BUILD_SCRIPT_PATH = 'scripts/build/bootstrap/incremental_build_util.py' +PIPELINE_RETRY_ATTEMPTS = 3 EMPTY_JSON = readJSON text: '{}' @@ -502,64 +503,75 @@ def CreateTeardownStage(Map environmentVars) { def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVars, String branchName, String pipelineName, String repositoryName, String projectName, boolean onlyMountEBSVolume = false) { def nodeLabel = envVars['NODE_LABEL'] return { - node("${nodeLabel}") { - if(isUnix()) { // Has to happen inside a node - envVars['IS_UNIX'] = 1 - } - withEnv(GetEnvStringList(envVars)) { - def build_job_name = build_job.key - try { - CreateSetupStage(pipelineConfig, snapshot, repositoryName, projectName, pipelineName, branchName, platform.key, build_job.key, envVars, onlyMountEBSVolume).call() + def currentResult = '' + def currentException = '' + retry(PIPELINE_RETRY_ATTEMPTS) { + node("${nodeLabel}") { + if(isUnix()) { // Has to happen inside a node + envVars['IS_UNIX'] = 1 + } + withEnv(GetEnvStringList(envVars)) { + def build_job_name = build_job.key + try { + CreateSetupStage(pipelineConfig, snapshot, repositoryName, projectName, pipelineName, branchName, platform.key, build_job.key, envVars, onlyMountEBSVolume).call() - if(build_job.value.steps) { //this is a pipe with many steps so create all the build stages - build_job.value.steps.each { build_step -> - build_job_name = build_step - envVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, platform.value.build_types[build_step].PIPELINE_ENV ?: EMPTY_JSON, pipelineName) - try { - CreateBuildStage(pipelineConfig, platform.key, build_step, envVars).call() + if(build_job.value.steps) { //this is a pipe with many steps so create all the build stages + build_job.value.steps.each { build_step -> + build_job_name = build_step + envVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, platform.value.build_types[build_step].PIPELINE_ENV ?: EMPTY_JSON, pipelineName) + try { + CreateBuildStage(pipelineConfig, platform.key, build_step, envVars).call() + } + catch (Exception e) { + if (envVars['NONBLOCKING_STEP']?.toBoolean()) { + unstable(message: "Build step ${build_step} failed but it's a non-blocking step in build job ${build_job.key}") + } else { + throw e + } + } } - catch (Exception e) { - if (envVars['NONBLOCKING_STEP']?.toBoolean()) { - unstable(message: "Build step ${build_step} failed but it's a non-blocking step in build job ${build_job.key}") - } - else { - error "FAILURE: ${e}" - } + } else { + CreateBuildStage(pipelineConfig, platform.key, build_job.key, envVars).call() + } + } + catch(Exception e) { + if (e instanceof org.jenkinsci.plugins.workflow.steps.FlowInterruptedException) { + def causes = e.getCauses().toString() + if (causes.contains('RemovedNodeCause')) { + error "Node disconnected during build: ${e}" // Error raised to retry stage on a new node } } - } else { - CreateBuildStage(pipelineConfig, platform.key, build_job.key, envVars).call() + // All other errors will be raised outside the retry block + currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE' + currentException = e.toString() } - } - catch(Exception e) { - // https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/Result.java - // {SUCCESS,UNSTABLE,FAILURE,NOT_BUILT,ABORTED} - def currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE' - if (currentResult == 'FAILURE') { - currentBuild.result = 'FAILURE' - error "FAILURE: ${e}" - } else if (currentResult == 'UNSTABLE') { - currentBuild.result = 'UNSTABLE' - unstable(message: "UNSTABLE: ${e}") + finally { + def params = platform.value.build_types[build_job_name].PARAMETERS + if (env.MARS_REPO && params && params.containsKey('TEST_METRICS') && params.TEST_METRICS == 'True') { + def output_directory = params.OUTPUT_DIRECTORY + def configuration = params.CONFIGURATION + CreateTestMetricsStage(pipelineConfig, branchName, envVars, build_job_name, output_directory, configuration).call() + } + if (params && params.containsKey('TEST_RESULTS') && params.TEST_RESULTS == 'True') { + CreateExportTestResultsStage(pipelineConfig, platform.key, build_job_name, envVars, params).call() + } + if (params && params.containsKey('TEST_SCREENSHOTS') && params.TEST_SCREENSHOTS == 'True' && currentResult == 'FAILURE') { + CreateExportTestScreenshotsStage(pipelineConfig, platform.key, build_job_name, envVars, params).call() + } + CreateTeardownStage(envVars).call() } } - finally { - def params = platform.value.build_types[build_job_name].PARAMETERS - if (env.MARS_REPO && params && params.containsKey('TEST_METRICS') && params.TEST_METRICS == 'True') { - def output_directory = params.OUTPUT_DIRECTORY - def configuration = params.CONFIGURATION - CreateTestMetricsStage(pipelineConfig, branchName, envVars, build_job_name, output_directory, configuration).call() - } - if (params && params.containsKey('TEST_RESULTS') && params.TEST_RESULTS == 'True') { - CreateExportTestResultsStage(pipelineConfig, platform.key, build_job_name, envVars, params).call() - } - if (params && params.containsKey('TEST_SCREENSHOTS') && params.TEST_SCREENSHOTS == 'True' && currentResult == 'FAILURE') { - CreateExportTestScreenshotsStage(pipelineConfig, platform.key, build_job_name, envVars, params).call() - } - CreateTeardownStage(envVars).call() - } } } + // https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/Result.java + // {SUCCESS,UNSTABLE,FAILURE,NOT_BUILT,ABORTED} + if (currentResult == 'FAILURE') { + currentBuild.result = 'FAILURE' + error "FAILURE: ${currentException}" + } else if (currentResult == 'UNSTABLE') { + currentBuild.result = 'UNSTABLE' + unstable(message: "UNSTABLE: ${currentException}") + } } } diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 02bd01038a..3848e6f980 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -360,7 +360,7 @@ "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DLY_INSTALLER_WIX_ROOT=\"!WIX! \"", - "EXTRA_CMAKE_OPTIONS": "-DLY_INSTALLER_AUTO_GEN_TAG=ON -DLY_INSTALLER_DOWNLOAD_URL=https://dkb1uj4hs9ikv.cloudfront.net -DLY_INSTALLER_LICENSE_URL=https://www.o3debinaries.org/license -DLY_INSTALLER_3RD_PARTY_LICENSE_URL=https://dkb1uj4hs9ikv.cloudfront.net/SPDX-Licenses.txt", + "EXTRA_CMAKE_OPTIONS": "-DLY_INSTALLER_AUTO_GEN_TAG=ON -DLY_INSTALLER_DOWNLOAD_URL=https://www.o3debinaries.org -DLY_INSTALLER_LICENSE_URL=https://www.o3debinaries.org/license", "CPACK_BUCKET": "spectra-prism-staging-us-west-2", "CMAKE_LY_PROJECTS": "", "CMAKE_TARGET": "ALL_BUILD",