diff --git a/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt index 26eb96e7ec..5c85dda9c0 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/Atom/CMakeLists.txt @@ -47,4 +47,18 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED) COMPONENT Atom ) + ly_add_pytest( + NAME AutomatedTesting::Atom_TestSuite_Benchmark_GPU + TEST_SUITE main + TEST_REQUIRES gpu + TEST_SERIAL + TIMEOUT 700 + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Benchmark_GPU.py + RUNTIME_DEPENDENCIES + AssetProcessor + AutomatedTesting.Assets + Editor + COMPONENT + Atom + ) endif() diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Benchmark_GPU.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Benchmark_GPU.py new file mode 100644 index 0000000000..08c8f4b0e7 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Benchmark_GPU.py @@ -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 +""" +import logging +import os + +import pytest + +import editor_python_test_tools.hydra_test_utils as hydra +from ly_test_tools.benchmark.data_aggregator import BenchmarkDataAggregator + +logger = logging.getLogger(__name__) + + +@pytest.mark.parametrize('rhi', ['dx12', 'vulkan']) +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +@pytest.mark.parametrize("launcher_platform", ["windows_editor"]) +@pytest.mark.parametrize("level", ["AtomFeatureIntegrationBenchmark"]) +class TestPerformanceBenchmarkSuite(object): + def test_AtomFeatureIntegrationBenchmarkTest_UploadMetrics( + self, request, editor, workspace, rhi, project, launcher_platform, level): + """ + Please review the hydra script run by this test for more specific test info. + Tests the performance of the Simple level. + """ + expected_lines = [ + "Benchmark metadata captured.", + "Pass timestamps captured.", + "CPU frame time captured.", + "Captured data successfully.", + "Exited game mode" + ] + + unexpected_lines = [ + "Failed to capture data.", + "Failed to capture pass timestamps.", + "Failed to capture CPU frame time.", + "Failed to capture benchmark metadata." + ] + + hydra.launch_and_validate_results( + request, + os.path.join(os.path.dirname(__file__), "tests"), + editor, + "hydra_GPUTest_AtomFeatureIntegrationBenchmark.py", + timeout=600, + expected_lines=expected_lines, + unexpected_lines=unexpected_lines, + halt_on_unexpected=True, + cfg_args=[level], + null_renderer=False, + enable_prefab_system=False, + ) + + aggregator = BenchmarkDataAggregator(workspace, logger, 'periodic') + aggregator.upload_metrics(rhi) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py index 14d850245c..58d08b14a5 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main_GPU.py @@ -11,8 +11,7 @@ import pytest import editor_python_test_tools.hydra_test_utils as hydra import ly_test_tools.environment.file_system as file_system -from ly_test_tools.benchmark.data_aggregator import BenchmarkDataAggregator -from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite +from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorTestSuite from Atom.atom_utils.atom_component_helper import compare_screenshot_to_golden_image, golden_images_directory DEFAULT_SUBFOLDER_PATH = 'user/PythonTests/Automated/Screenshots' @@ -23,133 +22,137 @@ logger = logging.getLogger(__name__) @pytest.mark.parametrize("launcher_platform", ['windows_editor']) class TestAutomation(EditorTestSuite): # Remove -autotest_mode from global_extra_cmdline_args since we need rendering for these tests. - global_extra_cmdline_args = ["-BatchMode"] # Default is ["-BatchMode", "-autotest_mode"] - + global_extra_cmdline_args = ["-autotest_mode"] # Default is ["-BatchMode", "-autotest_mode"] + use_null_renderer = False # Default is True enable_prefab_system = False - @pytest.mark.test_case_id("C34603773") - class AtomGPU_BasicLevelSetup_SetsUpLevel(EditorSharedTest): - use_null_renderer = False # Default is True - screenshot_name = "AtomBasicLevelSetup.ppm" - test_screenshots = [] # Gets set by setup() - screenshot_directory = "" # Gets set by setup() - - # Clear existing test screenshots before starting test. - def setup(self, workspace): - screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH) - test_screenshots = [os.path.join(screenshot_directory, self.screenshot_name)] - file_system.delete(test_screenshots, True, True) - - golden_images = [os.path.join(golden_images_directory(), screenshot_name)] - - from Atom.tests import hydra_AtomGPU_BasicLevelSetup as test_module - - assert compare_screenshot_to_golden_image(screenshot_directory, test_screenshots, golden_images, 0.99) is True - - @pytest.mark.test_case_id("C34525095") - class AtomGPU_LightComponent_AreaLightScreenshotsMatchGoldenImages(EditorSharedTest): - use_null_renderer = False # Default is True - screenshot_names = [ - "AreaLight_1.ppm", - "AreaLight_2.ppm", - "AreaLight_3.ppm", - "AreaLight_4.ppm", - "AreaLight_5.ppm", - ] - test_screenshots = [] # Gets set by setup() - screenshot_directory = "" # Gets set by setup() - - # Clear existing test screenshots before starting test. - def setup(self, workspace): - screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH) - for screenshot in self.screenshot_names: - screenshot_path = os.path.join(screenshot_directory, screenshot) - self.test_screenshots.append(screenshot_path) - file_system.delete(self.test_screenshots, True, True) - + @staticmethod + def screenshot_setup(screenshot_directory, screenshot_names): + """ + :param screenshot_names: list of screenshot file names with extensions + :return: tuple test_screenshots, golden_images each a list of full file paths + """ + test_screenshots = [] golden_images = [] + for screenshot in screenshot_names: + screenshot_path = os.path.join(screenshot_directory, screenshot) + test_screenshots.append(screenshot_path) + file_system.delete(test_screenshots, True, True) for golden_image in screenshot_names: golden_image_path = os.path.join(golden_images_directory(), golden_image) golden_images.append(golden_image_path) + return test_screenshots, golden_images + + @pytest.mark.test_case_id("C34525095") + class AtomGPU_LightComponent_AreaLightScreenshotsMatchGoldenImages_DX12(EditorSingleTest): from Atom.tests import hydra_AtomGPU_AreaLightScreenshotTest as test_module - assert compare_screenshot_to_golden_image(screenshot_directory, test_screenshots, golden_images, 0.99) is True + extra_cmdline_args = ["-rhi=dx12"] + + # Custom setup/teardown to remove old screenshots and establish paths to golden images + def setup(self, request, workspace, editor, editor_test_results, launcher_platform): + self.screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH) + self.screenshot_names = [ + "AreaLight_1.ppm", + "AreaLight_2.ppm", + "AreaLight_3.ppm", + "AreaLight_4.ppm", + "AreaLight_5.ppm", + ] + self.test_screenshots, self.golden_images = TestAutomation.screenshot_setup( + screenshot_directory=self.screenshot_directory, + screenshot_names=self.screenshot_names) + + def wrap_run(self, request, workspace, editor, editor_test_results, launcher_platform): + yield + assert compare_screenshot_to_golden_image(self.screenshot_directory, + self.test_screenshots, + self.golden_images, + similarity_threshold=0.96) is True - @pytest.mark.test_case_id("C34525110") - class AtomGPU_LightComponent_SpotLightScreenshotsMatchGoldenImages(EditorSharedTest): - use_null_renderer = False # Default is True - screenshot_names = [ - "SpotLight_1.ppm", - "SpotLight_2.ppm", - "SpotLight_3.ppm", - "SpotLight_4.ppm", - "SpotLight_5.ppm", - "SpotLight_6.ppm", - ] - test_screenshots = [] # Gets set by setup() - screenshot_directory = "" # Gets set by setup() - - # Clear existing test screenshots before starting test. - def setup(self, workspace): - screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH) - for screenshot in self.screenshot_names: - screenshot_path = os.path.join(screenshot_directory, screenshot) - self.test_screenshots.append(screenshot_path) - file_system.delete(self.test_screenshots, True, True) + @pytest.mark.test_case_id("C34525095") + class AtomGPU_LightComponent_AreaLightScreenshotsMatchGoldenImages_Vulkan(EditorSingleTest): + from Atom.tests import hydra_AtomGPU_AreaLightScreenshotTest as test_module - golden_images = [] - for golden_image in screenshot_names: - golden_image_path = os.path.join(golden_images_directory(), golden_image) - golden_images.append(golden_image_path) + extra_cmdline_args = ["-rhi=vulkan"] + + # Custom setup/teardown to remove old screenshots and establish paths to golden images + def setup(self, request, workspace, editor, editor_test_results, launcher_platform): + self.screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH) + self.screenshot_names = [ + "AreaLight_1.ppm", + "AreaLight_2.ppm", + "AreaLight_3.ppm", + "AreaLight_4.ppm", + "AreaLight_5.ppm", + ] + self.test_screenshots, self.golden_images = TestAutomation.screenshot_setup( + screenshot_directory=self.screenshot_directory, + screenshot_names=self.screenshot_names) + + def wrap_run(self, request, workspace, editor, editor_test_results, launcher_platform): + yield + assert compare_screenshot_to_golden_image(self.screenshot_directory, + self.test_screenshots, + self.golden_images, + similarity_threshold=0.96) is True + @pytest.mark.test_case_id("C34525110") + class AtomGPU_LightComponent_SpotLightScreenshotsMatchGoldenImages_DX12(EditorSingleTest): from Atom.tests import hydra_AtomGPU_SpotLightScreenshotTest as test_module - assert compare_screenshot_to_golden_image(screenshot_directory, test_screenshots, golden_images, 0.99) is True + extra_cmdline_args = ["-rhi=dx12"] + + # Custom setup/teardown to remove old screenshots and establish paths to golden images + def setup(self, request, workspace, editor, editor_test_results, launcher_platform): + self.screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH) + self.screenshot_names = [ + "SpotLight_1.ppm", + "SpotLight_2.ppm", + "SpotLight_3.ppm", + "SpotLight_4.ppm", + "SpotLight_5.ppm", + "SpotLight_6.ppm", + ] + self.test_screenshots, self.golden_images = TestAutomation.screenshot_setup( + screenshot_directory=self.screenshot_directory, + screenshot_names=self.screenshot_names) + + def wrap_run(self, request, workspace, editor, editor_test_results, launcher_platform): + yield + assert compare_screenshot_to_golden_image(self.screenshot_directory, + self.test_screenshots, + self.golden_images, + similarity_threshold=0.96) is True + @pytest.mark.test_case_id("C34525110") + class AtomGPU_LightComponent_SpotLightScreenshotsMatchGoldenImages_Vulkan(EditorSingleTest): + from Atom.tests import hydra_AtomGPU_SpotLightScreenshotTest as test_module -@pytest.mark.parametrize('rhi', ['dx12', 'vulkan']) -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("launcher_platform", ["windows_editor"]) -@pytest.mark.parametrize("level", ["AtomFeatureIntegrationBenchmark"]) -class TestPerformanceBenchmarkSuite(object): - def test_AtomFeatureIntegrationBenchmark( - self, request, editor, workspace, rhi, project, launcher_platform, level): - """ - Please review the hydra script run by this test for more specific test info. - Tests the performance of the Simple level. - """ - expected_lines = [ - "Benchmark metadata captured.", - "Pass timestamps captured.", - "CPU frame time captured.", - "Captured data successfully.", - "Exited game mode" - ] - - unexpected_lines = [ - "Failed to capture data.", - "Failed to capture pass timestamps.", - "Failed to capture CPU frame time.", - "Failed to capture benchmark metadata." - ] - - hydra.launch_and_validate_results( - request, - os.path.join(os.path.dirname(__file__), "tests"), - editor, - "hydra_GPUTest_AtomFeatureIntegrationBenchmark.py", - timeout=600, - expected_lines=expected_lines, - unexpected_lines=unexpected_lines, - halt_on_unexpected=True, - cfg_args=[level], - null_renderer=False, - enable_prefab_system=False, - ) - - aggregator = BenchmarkDataAggregator(workspace, logger, 'periodic') - aggregator.upload_metrics(rhi) + extra_cmdline_args = ["-rhi=vulkan"] + + # Custom setup/teardown to remove old screenshots and establish paths to golden images + def setup(self, request, workspace, editor, editor_test_results, launcher_platform): + self.screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH) + self.screenshot_names = [ + "SpotLight_1.ppm", + "SpotLight_2.ppm", + "SpotLight_3.ppm", + "SpotLight_4.ppm", + "SpotLight_5.ppm", + "SpotLight_6.ppm", + ] + self.test_screenshots, self.golden_images = TestAutomation.screenshot_setup( + screenshot_directory=self.screenshot_directory, + screenshot_names=self.screenshot_names) + + def wrap_run(self, request, workspace, editor, editor_test_results, launcher_platform): + yield + assert compare_screenshot_to_golden_image(self.screenshot_directory, + self.test_screenshots, + self.golden_images, + similarity_threshold=0.96) is True @pytest.mark.parametrize("project", ["AutomatedTesting"]) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py index 5fdd4c810d..42310ee983 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_component_helper.py @@ -86,9 +86,8 @@ def compare_screenshot_similarity( if create_zip_archive: create_screenshots_archive(screenshot_directory) result = ( - f"When comparing the test_screenshot: '{test_screenshot}' " - f"to golden_image: '{golden_image}' the mean similarity of '{mean_similarity}' " - f"was lower than the similarity threshold of '{similarity_threshold}'. ") + f"When comparing the test_screenshot: '{test_screenshot}' to golden_image: '{golden_image}'.\n" + f"The mean similarity ({mean_similarity}) was lower than the similarity threshold ({similarity_threshold})") return result @@ -123,7 +122,9 @@ def initial_viewport_setup(screen_width=1280, screen_height=720): import azlmbr.legacy.general as general general.set_viewport_size(screen_width, screen_height) + general.idle_wait_frames(1) general.update_viewport() + general.idle_wait_frames(1) def enter_exit_game_mode_take_screenshot(screenshot_name, enter_game_tuple, exit_game_tuple, timeout_in_seconds=4): @@ -137,13 +138,18 @@ def enter_exit_game_mode_take_screenshot(screenshot_name, enter_game_tuple, exit """ import azlmbr.legacy.general as general - from editor_python_test_tools.utils import TestHelper + from editor_python_test_tools.utils import TestHelper, Report from Atom.atom_utils.screenshot_utils import ScreenshotHelper + screenshot_helper = ScreenshotHelper(general.idle_wait_frames) TestHelper.enter_game_mode(enter_game_tuple) TestHelper.wait_for_condition(function=lambda: general.is_in_game_mode(), timeout_in_seconds=timeout_in_seconds) - ScreenshotHelper(general.idle_wait_frames).capture_screenshot_blocking(screenshot_name) + screenshot_helper.prepare_viewport_for_screenshot(1920, 1080) + success_screenshot = TestHelper.wait_for_condition( + function=lambda: screenshot_helper.capture_screenshot_blocking(screenshot_name), + timeout_in_seconds=timeout_in_seconds) + Report.result(("Screenshot taken", "Screenshot failed to be taken"), success_screenshot) TestHelper.exit_game_mode(exit_game_tuple) TestHelper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=timeout_in_seconds) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py index c3c1c76611..90f9399c1c 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py @@ -162,11 +162,11 @@ def select_model_config(configname): azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SelectModelPresetByName", configname) -def destroy_main_window(): +def exit(): """ - Closes the Material Editor window + Closes the Material Editor """ - azlmbr.atomtools.AtomToolsMainWindowFactoryRequestBus(azlmbr.bus.Broadcast, "DestroyMainWindow") + azlmbr.atomtools.general.exit() def wait_for_condition(function, timeout_in_seconds=1.0): diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomGPU_BasicLevelSetup.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomGPU_BasicLevelSetup.py index b15e5b2131..c910662222 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomGPU_BasicLevelSetup.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomGPU_BasicLevelSetup.py @@ -107,10 +107,8 @@ def AtomGPU_BasicLevelSetup_SetsUpLevel(): 18. Add Mesh component to Sphere Entity and set the Mesh Asset property for the Mesh component. 19. Create a Camera Entity as a child entity of the Default Level Entity then add a Camera component. 20. Set the Camera Entity rotation value and set the Camera component Field of View value. - 21. Enter game mode. - 22. Take screenshot. - 23. Exit game mode. - 24. Look for errors. + 21. Enter/Exit game mode taking screenshot. + 22. Look for errors. :return: None """ @@ -127,7 +125,7 @@ def AtomGPU_BasicLevelSetup_SetsUpLevel(): from Atom.atom_utils.atom_constants import AtomComponentProperties from Atom.atom_utils.atom_component_helper import initial_viewport_setup - from Atom.atom_utils.screenshot_utils import ScreenshotHelper + from Atom.atom_utils.atom_component_helper import enter_exit_game_mode_take_screenshot DEGREE_RADIAN_FACTOR = 0.0174533 SCREENSHOT_NAME = "AtomBasicLevelSetup" @@ -300,18 +298,10 @@ def AtomGPU_BasicLevelSetup_SetsUpLevel(): Report.result(Tests.camera_fov_set, camera_component.get_component_property_value( AtomComponentProperties.camera('Field of view')) == camera_fov_value) - # 21. Enter game mode. - TestHelper.enter_game_mode(Tests.enter_game_mode) - TestHelper.wait_for_condition(function=lambda: general.is_in_game_mode(), timeout_in_seconds=4.0) + # 21. Enter/Exit game mode taking screenshot. + enter_exit_game_mode_take_screenshot(f"{SCREENSHOT_NAME}.ppm", Tests.enter_game_mode, Tests.exit_game_mode) - # 22. Take screenshot. - ScreenshotHelper(general.idle_wait_frames).capture_screenshot_blocking(f"{SCREENSHOT_NAME}.ppm") - - # 23. Exit game mode. - TestHelper.exit_game_mode(Tests.exit_game_mode) - TestHelper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=4.0) - - # 24. Look for errors. + # 22. Look for errors. TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) for error_info in error_tracer.errors: Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py index bd00a84919..f881660f33 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py @@ -214,7 +214,7 @@ def run(): (not material_editor.is_open(document1_id)) and (not material_editor.is_open(document2_id)) and (not material_editor.is_open(document3_id)), 2.0) - material_editor.destroy_main_window() + material_editor.exit() if __name__ == "__main__": diff --git a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt index 26c03e78c2..421d095be7 100644 --- a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt @@ -67,3 +67,6 @@ add_subdirectory(Multiplayer) ## Integration tests for editor testing framework ## add_subdirectory(editor_test_testing) + +## Performance ## +add_subdirectory(Performance) diff --git a/AutomatedTesting/Gem/PythonTests/Performance/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/Performance/CMakeLists.txt new file mode 100644 index 0000000000..c2ec14db9d --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Performance/CMakeLists.txt @@ -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 +# +# + +if(NOT PAL_TRAIT_BUILD_TESTS_SUPPORTED OR NOT PAL_TRAIT_BUILD_HOST_TOOLS) + return() +endif() + +ly_add_pytest( + NAME AutomatedTesting::EditorLevelLoadingPerfTests_DX12 + TEST_SUITE periodic + TEST_REQUIRES gpu + TEST_SERIAL + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Periodic_DX12.py + RUNTIME_DEPENDENCIES + Legacy::Editor + AZ::AssetProcessor + AutomatedTesting.Assets + COMPONENT + Performance +) + +ly_add_pytest( + NAME AutomatedTesting::EditorLevelLoadingPerfTests_Vulkan + TEST_SUITE periodic + TEST_REQUIRES gpu + TEST_SERIAL + PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Periodic_Vulkan.py + RUNTIME_DEPENDENCIES + Legacy::Editor + AZ::AssetProcessor + AutomatedTesting.Assets + COMPONENT + Performance +) diff --git a/AutomatedTesting/Gem/PythonTests/Performance/TestSuite_Periodic_DX12.py b/AutomatedTesting/Gem/PythonTests/Performance/TestSuite_Periodic_DX12.py new file mode 100644 index 0000000000..ac3152a090 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Performance/TestSuite_Periodic_DX12.py @@ -0,0 +1,31 @@ +""" +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 + +""" + +# This suite consists of all test cases that are passing and have been verified. + +import pytest +import os +import sys + +from ly_test_tools.o3de.editor_test import EditorTestSuite, EditorSingleTest + +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +@pytest.mark.parametrize("launcher_platform", ['windows_editor']) +class TestAutomation(EditorTestSuite): + + class Time_EditorLevelLoading_10KEntityCpuPerfTest(EditorSingleTest): + extra_cmdline_args = ['-rhi=dx12'] + use_null_renderer = False # needs renderer to validate test + + from .tests import EditorLevelLoading_10KEntityCpuPerfTest as test_module + + class Time_EditorLevelLoading_10kVegInstancesTest(EditorSingleTest): + extra_cmdline_args = ['-rhi=dx12'] + use_null_renderer = False # needs renderer to validate test + + from .tests import EditorLevelLoading_10kVegInstancesTest as test_module diff --git a/AutomatedTesting/Gem/PythonTests/Performance/TestSuite_Periodic_Vulkan.py b/AutomatedTesting/Gem/PythonTests/Performance/TestSuite_Periodic_Vulkan.py new file mode 100644 index 0000000000..c71ede27dc --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Performance/TestSuite_Periodic_Vulkan.py @@ -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 + +""" + +# This suite consists of all test cases that are passing and have been verified. + +import pytest +import os +import sys + +from ly_test_tools.o3de.editor_test import EditorTestSuite, EditorSingleTest + +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +@pytest.mark.parametrize("launcher_platform", ['windows_editor']) +class TestAutomation(EditorTestSuite): + + class Time_EditorLevelLoading_10KEntityCpuPerfTest(EditorSingleTest): + # there is currently a huge discrepancy loading this level with vulkan compared to dx12 which requires the 10 min timeout + # this should be removed once that issue has been sorted out + timeout = 600 + extra_cmdline_args = ['-rhi=vulkan'] + use_null_renderer = False # needs renderer to validate test + + from .tests import EditorLevelLoading_10KEntityCpuPerfTest as test_module + + class Time_EditorLevelLoading_10kVegInstancesTest(EditorSingleTest): + extra_cmdline_args = ['-rhi=vulkan'] + use_null_renderer = False # needs renderer to validate test + + from .tests import EditorLevelLoading_10kVegInstancesTest as test_module diff --git a/AutomatedTesting/Gem/PythonTests/Performance/__init__.py b/AutomatedTesting/Gem/PythonTests/Performance/__init__.py new file mode 100644 index 0000000000..f5193b300e --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Performance/__init__.py @@ -0,0 +1,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 +""" diff --git a/AutomatedTesting/Gem/PythonTests/Performance/tests/EditorLevelLoading_10KEntityCpuPerfTest.py b/AutomatedTesting/Gem/PythonTests/Performance/tests/EditorLevelLoading_10KEntityCpuPerfTest.py new file mode 100644 index 0000000000..fb7707044a --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Performance/tests/EditorLevelLoading_10KEntityCpuPerfTest.py @@ -0,0 +1,15 @@ +""" +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 +""" + +from Performance.utils.perf_timer import time_editor_level_loading + +def EditorLevelLoading_10KEntityCpuPerfTest(): + time_editor_level_loading('Performance', '10KEntityCpuPerfTest') + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(EditorLevelLoading_10KEntityCpuPerfTest) diff --git a/AutomatedTesting/Gem/PythonTests/Performance/tests/EditorLevelLoading_10kVegInstancesTest.py b/AutomatedTesting/Gem/PythonTests/Performance/tests/EditorLevelLoading_10kVegInstancesTest.py new file mode 100644 index 0000000000..1c71cc806e --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Performance/tests/EditorLevelLoading_10kVegInstancesTest.py @@ -0,0 +1,15 @@ +""" +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 +""" + +from Performance.utils.perf_timer import time_editor_level_loading + +def EditorLevelLoading_10kVegInstancesTest(): + time_editor_level_loading('Performance', '10kVegInstancesTest') + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(EditorLevelLoading_10kVegInstancesTest) diff --git a/AutomatedTesting/Gem/PythonTests/Performance/tests/__init__.py b/AutomatedTesting/Gem/PythonTests/Performance/tests/__init__.py new file mode 100644 index 0000000000..f5193b300e --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Performance/tests/__init__.py @@ -0,0 +1,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 +""" diff --git a/AutomatedTesting/Gem/PythonTests/Performance/utils/__init__.py b/AutomatedTesting/Gem/PythonTests/Performance/utils/__init__.py new file mode 100644 index 0000000000..f5193b300e --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Performance/utils/__init__.py @@ -0,0 +1,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 +""" diff --git a/AutomatedTesting/Gem/PythonTests/Performance/utils/perf_timer.py b/AutomatedTesting/Gem/PythonTests/Performance/utils/perf_timer.py new file mode 100644 index 0000000000..54497e96b2 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Performance/utils/perf_timer.py @@ -0,0 +1,72 @@ +""" +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 +""" + +import time + +ENTER_MSG = ("Entered game mode", "Failed to enter game mode") +EXIT_MSG = ("Exited game mode", "Couldn't exit game mode") + +class Timer: + unit_divisor = 60 + hour_divisor = unit_divisor * unit_divisor + + def start(self): + self._start_time = time.perf_counter() + + def log_time(self, message): + from editor_python_test_tools.utils import Report + + elapsed_time = time.perf_counter() - self._start_time + hours = int(elapsed_time / Timer.hour_divisor) + minutes = int(elapsed_time % Timer.hour_divisor / Timer.unit_divisor) + seconds = elapsed_time % Timer.unit_divisor + + Report.info(f'{message}: {hours:0>2d}:{minutes:0>2d}:{seconds:0>5.2f}\n') + +def time_editor_level_loading(level_dir, level_name): + + """ + Summary: + Time how long it takes to load an arbitrary level, entering game mode, and exiting game mode + + Level Description: + Preferably a level with a large number of entities + + Expected Behavior: + Level loads within a reasonable time frame e.i. doesn't trip the framework timeout + + Benchmark Steps: + 1) Time opening the level + 2) Time entering game mode + 3) Time exiting game mode + 4) Close the editor + + :return: None + """ + from editor_python_test_tools.utils import TestHelper as helper + + timer = Timer() + + helper.init_idle() + + # 1) Open level + timer.start() + helper.open_level(level_dir, level_name) + timer.log_time('Level load time') + + # 2) Time how long it takes to enter game mode + timer.start() + helper.enter_game_mode(ENTER_MSG) + timer.log_time('Enter game mode') + + # 3) Exit game mode + timer.start() + helper.exit_game_mode(EXIT_MSG) + timer.log_time('Exit game mode') + + # 4) Close the editor + helper.close_editor() diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ModifiesSurfaces.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ModifiesSurfaces.py new file mode 100644 index 0000000000..23c144a3e2 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/EditorScripts/ImageGradient_ModifiesSurfaces.py @@ -0,0 +1,114 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + + +class Tests: + image_gradient_entity_created = ( + "Image Gradient entity created", + "Failed to create Image Gradient entity", + ) + image_gradient_assigned = ( + "Successfully assigned image gradient asset", + "Failed to assign image gradient asset" + ) + instance_validation = ( + "Found the expected number of instances", + "Found an unexpected number of instances" + ) + + +def ImageGradient_ModifiesSurfaces(): + """ + Summary: + This test verifies that an Image Gradient + Gradient Surface Tag Emitter properly modifies surfaces. + + Expected Behavior: + Vegetation is used to verify expected surface modification. + + Test Steps: + 1) Open a level + 2) Create an entity with Image Gradient, Gradient Transform Modifier, Shape Reference, and Gradient Surface Tag + Emitter components with an Image asset assigned. + 3) Create a Vegetation Layer Spawner setup to plant on the generated surface. + 4) Update all surface tag references + 5) Validate expected instances planted on the modified surface. + + :return: None + """ + + import os + + import azlmbr.bus as bus + import azlmbr.entity as EntityId + import azlmbr.editor as editor + import azlmbr.math as math + import azlmbr.surface_data as surface_data + + import editor_python_test_tools.hydra_editor_utils as hydra + from editor_python_test_tools.asset_utils import Asset + from editor_python_test_tools.editor_entity_utils import EditorEntity + from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg + from editor_python_test_tools.utils import Report + from editor_python_test_tools.utils import TestHelper as helper + + # 1) Open an existing simple level + hydra.open_base_level() + + # 2) Create an entity with required Image Gradient + Surface Tag Emitter components and assign image asset + components_to_add = ["Image Gradient", "Gradient Transform Modifier", "Shape Reference", + "Gradient Surface Tag Emitter"] + entity_position = math.Vector3(0.0, 0.0, 0.0) + new_entity_id = editor.ToolsApplicationRequestBus( + bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId() + ) + Report.critical_result(Tests.image_gradient_entity_created, new_entity_id.IsValid()) + image_gradient_entity = EditorEntity.create_editor_entity_at(entity_position, "Image Gradient") + image_gradient_entity.add_components(components_to_add) + test_img_gradient_path = os.path.join("Assets", "ImageGradients", "image_grad_test_gsi.png.streamingimage") + asset = Asset.find_asset_by_path(test_img_gradient_path) + image_gradient_entity.components[0].set_component_property_value("Configuration|Image Asset", asset.id) + success = image_gradient_entity.components[0].get_component_property_value("Configuration|Image Asset") == asset.id + Report.result(Tests.image_gradient_assigned, success) + + # 3) Create vegetation and planting surface entities, and assign the Image Gradient entity's Shape Reference + + # Create vegetation entity + purple_flower_prefab_path = os.path.join("assets", "prefabs", "PurpleFlower.spawnable") + spawner_entity = dynveg.create_prefab_vegetation_area("Instance Spawner", entity_position, 50.0, 50.0, 10.0, + purple_flower_prefab_path) + spawner_entity.add_component("Vegetation Surface Mask Filter") + + # Create surface entity + dynveg.create_surface_entity("Box Shape", entity_position, 50.0, 50.0, 1.0) + + # Assign Image Gradient entity's Shape Reference + image_gradient_entity.components[2].set_component_property_value("Configuration|Shape Entity Id", spawner_entity.id) + + # 4) Assign surface tags to the required components + tag_list = [surface_data.SurfaceTag("terrain")] + + # Set the Veg Spawner entity's Surface Tag Mask Filter component to include the "terrain" tag + hydra.get_set_test(spawner_entity, 3, "Configuration|Inclusion|Surface Tags", tag_list) + + # Set the Image Gradient entity's Gradient Surface Tag Emitter component to modify the "terrain" tag + # NOTE: This requires a disable/re-enable of the component to force a refresh as assigning a tag via script does not + grad_surf_tag_emitter_component = image_gradient_entity.components[3] + grad_surf_tag_emitter_component.add_container_item("Configuration|Extended Tags", 0, tag_list[0]) + grad_surf_tag_emitter_component.set_enabled(False) + grad_surf_tag_emitter_component.set_enabled(True) + + # 5) Validate the expected number of vegetation instances. Instances should only spawn on the modified surface + num_expected_instances = 168 + success = helper.wait_for_condition(lambda: dynveg.validate_instance_count_in_entity_shape( + spawner_entity.id, num_expected_instances), 5.0) + Report.result(Tests.instance_validation, success) + + +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(ImageGradient_ModifiesSurfaces) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic.py index d4199c31f6..7482d2b04a 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/gradient_signal/TestSuite_Periodic.py @@ -48,6 +48,9 @@ class TestAutomation(EditorTestSuite): class test_GradientTransform_RequiresShape(EditorSharedTest): from .EditorScripts import GradientTransform_RequiresShape as test_module + class test_ImageGradient_ModifiesSurfaces(EditorSharedTest): + from .EditorScripts import ImageGradient_ModifiesSurfaces as test_module + class test_ImageGradient_ProcessedImageAssignedSuccessfully(EditorSharedTest): from .EditorScripts import ImageGradient_ProcessedImageAssignedSuccessfully as test_module diff --git a/AutomatedTesting/Passes/MainPipeline.pass b/AutomatedTesting/Passes/MainPipeline.pass index 39b992a11d..8ad4006570 100644 --- a/AutomatedTesting/Passes/MainPipeline.pass +++ b/AutomatedTesting/Passes/MainPipeline.pass @@ -460,6 +460,33 @@ } ] }, + { + "Name": "DiffuseProbeGridVisualizationCompositePass", + "TemplateName": "DiffuseProbeGridVisualizationCompositePassTemplate", + "Connections": [ + { + "LocalSlot": "VisualizationInput", + "AttachmentRef": { + "Pass": "OpaquePass", + "Attachment": "DiffuseProbeGridVisualization" + } + }, + { + "LocalSlot": "Depth", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "Depth" + } + }, + { + "LocalSlot": "ColorInputOutput", + "AttachmentRef": { + "Pass": "PostProcessPass", + "Attachment": "Output" + } + } + ] + }, { "Name": "AuxGeomPass", "TemplateName": "AuxGeomPassTemplate", @@ -468,8 +495,8 @@ { "LocalSlot": "ColorInputOutput", "AttachmentRef": { - "Pass": "PostProcessPass", - "Attachment": "Output" + "Pass": "DiffuseProbeGridVisualizationCompositePass", + "Attachment": "ColorInputOutput" } }, { diff --git a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.cpp b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.cpp index c5bf6a4d81..dd32b96b2c 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.cpp +++ b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.cpp @@ -360,18 +360,6 @@ void ReflectedPropertyControl::CreateItems(XmlNodeRef node, CVarBlockPtr& outBlo textureVar->Set(textureName); } } - else if (!azstricmp(type, "color")) - { - CSmartVariable colorVar; - AddVariable(group, colorVar, child->getTag(), humanReadableName.toUtf8().data(), strDescription.toUtf8().data(), func, pUserData, IVariable::DT_COLOR); - ColorB color; - if (child->getAttr("value", color)) - { - ColorF colorLinear = ColorGammaToLinear(QColor(color.r, color.g, color.b)); - Vec3 colorVec3(colorLinear.r, colorLinear.g, colorLinear.b); - colorVar->Set(colorVec3); - } - } } } diff --git a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyItem.cpp b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyItem.cpp index 0fcbac3204..b56a3e411d 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyItem.cpp +++ b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedPropertyItem.cpp @@ -243,9 +243,6 @@ void ReflectedPropertyItem::SetVariable(IVariable *var) case ePropertySelection: m_reflectedVarAdapter = new ReflectedVarEnumAdapter; break; - case ePropertyColor: - m_reflectedVarAdapter = new ReflectedVarColorAdapter; - break; case ePropertyUser: m_reflectedVarAdapter = new ReflectedVarUserAdapter; break; diff --git a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.cpp b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.cpp index 8eefbbc8eb..1e8d42acc0 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.cpp +++ b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.cpp @@ -312,50 +312,6 @@ void ReflectedVarVector4Adapter::SyncIVarToReflectedVar(IVariable *pVariable) pVariable->Set(Vec4(m_reflectedVar->m_value.GetX(), m_reflectedVar->m_value.GetY(), m_reflectedVar->m_value.GetZ(), m_reflectedVar->m_value.GetW())); } - -void ReflectedVarColorAdapter::SetVariable(IVariable *pVariable) -{ - m_reflectedVar.reset(new CReflectedVarColor(pVariable->GetHumanName().toUtf8().data())); - m_reflectedVar->m_description = pVariable->GetDescription().toUtf8().data(); -} - -void ReflectedVarColorAdapter::SyncReflectedVarToIVar(IVariable *pVariable) -{ - if (pVariable->GetType() == IVariable::VECTOR) - { - Vec3 v(0, 0, 0); - pVariable->Get(v); - const QColor col = ColorLinearToGamma(ColorF(v.x, v.y, v.z)); - m_reflectedVar->m_color.Set(static_cast(col.redF()), static_cast(col.greenF()), static_cast(col.blueF())); - } - else - { - int col(0); - pVariable->Get(col); - const QColor qcolor = ColorToQColor((uint32)col); - m_reflectedVar->m_color.Set(static_cast(qcolor.redF()), static_cast(qcolor.greenF()), static_cast(qcolor.blueF())); - } -} - -void ReflectedVarColorAdapter::SyncIVarToReflectedVar(IVariable *pVariable) -{ - if (pVariable->GetType() == IVariable::VECTOR) - { - ColorF colLin = ColorGammaToLinear(QColor::fromRgbF(m_reflectedVar->m_color.GetX(), m_reflectedVar->m_color.GetY(), m_reflectedVar->m_color.GetZ())); - pVariable->Set(Vec3(colLin.r, colLin.g, colLin.b)); - } - else - { - int ir = static_cast(m_reflectedVar->m_color.GetX() * 255.0f); - int ig = static_cast(m_reflectedVar->m_color.GetY() * 255.0f); - int ib = static_cast(m_reflectedVar->m_color.GetZ() * 255.0f); - - pVariable->Set(static_cast(RGB(ir, ig, ib))); - } -} - - - void ReflectedVarResourceAdapter::SetVariable(IVariable *pVariable) { m_reflectedVar.reset(new CReflectedVarResource(pVariable->GetHumanName().toUtf8().data())); diff --git a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.h b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.h index 807413226c..4efbab0bab 100644 --- a/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.h +++ b/Code/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.h @@ -186,21 +186,6 @@ AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING }; - -class EDITOR_CORE_API ReflectedVarColorAdapter - : public ReflectedVarAdapter -{ -public: - void SetVariable(IVariable* pVariable) override; - void SyncReflectedVarToIVar(IVariable* pVariable) override; - void SyncIVarToReflectedVar(IVariable* pVariable) override; - CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); } -private: -AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING - QScopedPointer m_reflectedVar; -AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING -}; - class EDITOR_CORE_API ReflectedVarResourceAdapter : public ReflectedVarAdapter { diff --git a/Code/Editor/Core/QtEditorApplication.cpp b/Code/Editor/Core/QtEditorApplication.cpp index 2439228476..8f7db74125 100644 --- a/Code/Editor/Core/QtEditorApplication.cpp +++ b/Code/Editor/Core/QtEditorApplication.cpp @@ -32,15 +32,6 @@ #include "Settings.h" #include "CryEdit.h" -enum -{ - // in milliseconds - GameModeIdleFrequency = 0, - EditorModeIdleFrequency = 1, - InactiveModeFrequency = 10, - UninitializedFrequency = 9999, -}; - Q_LOGGING_CATEGORY(InputDebugging, "o3de.editor.input") // internal, private namespace: @@ -234,18 +225,12 @@ namespace Editor EditorQtApplication::EditorQtApplication(int& argc, char** argv) : AzQtApplication(argc, argv) , m_stylesheet(new AzQtComponents::O3DEStylesheet(this)) - , m_idleTimer(new QTimer(this)) { - m_idleTimer->setInterval(UninitializedFrequency); - setWindowIcon(QIcon(":/Application/res/o3de_editor.ico")); // set the default key store for our preferences: setApplicationName("O3DE Editor"); - connect(m_idleTimer, &QTimer::timeout, this, &EditorQtApplication::maybeProcessIdle); - - connect(this, &QGuiApplication::applicationStateChanged, this, [this] { ResetIdleTimerInterval(PollState); }); installEventFilter(this); // Disable our debugging input helpers by default @@ -324,6 +309,10 @@ namespace Editor winapp->OnIdle(0); } } + if (m_applicationActive) + { + QTimer::singleShot(1, this, &EditorQtApplication::maybeProcessIdle); + } } void EditorQtApplication::InstallQtLogHandler() @@ -376,14 +365,6 @@ namespace Editor case eNotify_OnQuit: GetIEditor()->UnregisterNotifyListener(this); break; - - case eNotify_OnBeginGameMode: - // GetIEditor()->IsInGameMode() Isn't reliable when called from within the notification handler - ResetIdleTimerInterval(GameMode); - break; - case eNotify_OnEndGameMode: - ResetIdleTimerInterval(EditorMode); - break; } } @@ -456,55 +437,16 @@ namespace Editor void EditorQtApplication::EnableOnIdle(bool enable) { + m_applicationActive = enable; if (enable) { - if (m_idleTimer->interval() == UninitializedFrequency) - { - ResetIdleTimerInterval(); - } - - m_idleTimer->start(); - } - else - { - m_idleTimer->stop(); + QTimer::singleShot(0, this, &EditorQtApplication::maybeProcessIdle); } } bool EditorQtApplication::OnIdleEnabled() const { - if (m_idleTimer->interval() == UninitializedFrequency) - { - return false; - } - - return m_idleTimer->isActive(); - } - - void EditorQtApplication::ResetIdleTimerInterval(TimerResetFlag flag) - { - bool isInGameMode = flag == GameMode; - if (flag == PollState) - { - isInGameMode = GetIEditor() ? GetIEditor()->IsInGameMode() : false; - } - - // Game mode takes precedence over anything else - if (isInGameMode) - { - m_idleTimer->setInterval(GameModeIdleFrequency); - } - else - { - if (applicationState() & Qt::ApplicationActive) - { - m_idleTimer->setInterval(EditorModeIdleFrequency); - } - else - { - m_idleTimer->setInterval(InactiveModeFrequency); - } - } + return m_applicationActive; } bool EditorQtApplication::eventFilter(QObject* object, QEvent* event) diff --git a/Code/Editor/Core/QtEditorApplication.h b/Code/Editor/Core/QtEditorApplication.h index 28ee8ac14b..9cb03ec644 100644 --- a/Code/Editor/Core/QtEditorApplication.h +++ b/Code/Editor/Core/QtEditorApplication.h @@ -102,13 +102,6 @@ namespace Editor bool m_isMovingOrResizing = false; private: - enum TimerResetFlag - { - PollState, - GameMode, - EditorMode - }; - void ResetIdleTimerInterval(TimerResetFlag = PollState); static QColor InterpolateColors(QColor a, QColor b, float factor); void RefreshStyleSheet(); void InstallFilters(); @@ -125,7 +118,6 @@ namespace Editor QTranslator* m_editorTranslator = nullptr; QTranslator* m_assetBrowserTranslator = nullptr; - QTimer* const m_idleTimer = nullptr; AZ::UserSettingsProvider m_localUserSettings; @@ -133,5 +125,6 @@ namespace Editor QSet m_pressedKeys; bool m_activatedLocalUserSettings = false; + bool m_applicationActive = false; }; } // namespace editor diff --git a/Code/Editor/LogFile_mac.mm b/Code/Editor/LogFile_mac.mm index fb227314ad..3471f29958 100644 --- a/Code/Editor/LogFile_mac.mm +++ b/Code/Editor/LogFile_mac.mm @@ -26,8 +26,13 @@ QString graphicsCardName() // Create an iterator io_iterator_t iterator; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_12_0 + if (IOServiceGetMatchingServices(kIOMainPortDefault,matchDict, + &iterator) == kIOReturnSuccess) +#else if (IOServiceGetMatchingServices(kIOMasterPortDefault,matchDict, &iterator) == kIOReturnSuccess) +#endif { // Iterator for devices found io_registry_entry_t regEntry; diff --git a/Code/Editor/Platform/Windows/Editor/Core/QtEditorApplication_windows.cpp b/Code/Editor/Platform/Windows/Editor/Core/QtEditorApplication_windows.cpp index f8065af931..1fd7f29ca3 100644 --- a/Code/Editor/Platform/Windows/Editor/Core/QtEditorApplication_windows.cpp +++ b/Code/Editor/Platform/Windows/Editor/Core/QtEditorApplication_windows.cpp @@ -135,7 +135,7 @@ namespace Editor } widget = widget->parentWidget(); } - return false; + return nullptr; }; if (object == toolBarAt(QCursor::pos())) { diff --git a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp index 0ff9467f33..f19acd4707 100644 --- a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp +++ b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp @@ -44,6 +44,9 @@ class CXTPDockingPaneLayout; // Needed for settings.h #include #include #include +#include +#include +#include const char* AssetImporterWindow::s_documentationWebAddress = "http://docs.aws.amazon.com/lumberyard/latest/userguide/char-fbx-importer.html"; const AZ::Uuid AssetImporterWindow::s_browseTag = AZ::Uuid::CreateString("{C240D2E1-BFD2-4FFA-BB5B-CC0FA389A5D3}"); @@ -198,7 +201,7 @@ void AssetImporterWindow::Init() // Filling the initial browse prompt text to be programmatically set from available extensions AZStd::unordered_set extensions; EBUS_EVENT(AZ::SceneAPI::Events::AssetImportRequestBus, GetSupportedFileExtensions, extensions); - AZ_Assert(!extensions.empty(), "No file extensions defined for assets."); + AZ_Error(AZ::SceneAPI::Utilities::ErrorWindow, !extensions.empty(), "No file extensions defined for assets."); if (!extensions.empty()) { for (AZStd::string& extension : extensions) @@ -252,6 +255,7 @@ void AssetImporterWindow::OpenFileInternal(const AZStd::string& filePath) [this, filePath]() { m_assetImporterDocument->LoadScene(filePath); + UpdateSceneDisplay({}); }, [this]() { @@ -290,6 +294,11 @@ void AssetImporterWindow::UpdateClicked() AZ_Assert(!m_processingOverlay, "Attempted to update asset while processing is in progress."); return; } + else if (!m_scriptProcessorRuleFilename.empty()) + { + AZ_TracePrintf(AZ::SceneAPI::Utilities::WarningWindow, "A script updates the manifest; will not save."); + return; + } m_processingOverlay.reset(new ProcessingOverlayWidget(m_overlay.data(), ProcessingOverlayWidget::Layout::Exporting, s_browseTag)); connect(m_processingOverlay.data(), &ProcessingOverlayWidget::Closing, this, &AssetImporterWindow::ClearProcessingOverlay); @@ -383,6 +392,18 @@ void AssetImporterWindow::OnSceneResetRequested() m_rootDisplay->HandleSceneWasReset(m_assetImporterDocument->GetScene()); }, this); + // reset the script rule from the .assetinfo file if it exists + if (!m_scriptProcessorRuleFilename.empty()) + { + m_scriptProcessorRuleFilename.clear(); + if (QFile::exists(m_assetImporterDocument->GetScene()->GetManifestFilename().c_str())) + { + QFile file(m_assetImporterDocument->GetScene()->GetManifestFilename().c_str()); + file.remove(); + } + } + UpdateSceneDisplay({}); + m_processingOverlay.reset(new ProcessingOverlayWidget(m_overlay.data(), ProcessingOverlayWidget::Layout::Resetting, s_browseTag)); m_processingOverlay->SetAndStartProcessingHandler(asyncLoadHandler); m_processingOverlay->SetAutoCloseOnSuccess(true); @@ -390,6 +411,51 @@ void AssetImporterWindow::OnSceneResetRequested() m_processingOverlayIndex = m_processingOverlay->PushToOverlay(); } +void AssetImporterWindow::OnAssignScript() +{ + using namespace AZ::SceneAPI; + using namespace AZ::SceneAPI::Events; + using namespace AZ::SceneAPI::SceneUI; + using namespace AZ::SceneAPI::Utilities; + + // use QFileDialog to select a Python script to embed into a scene manifest file + QString pyFilename = QFileDialog::getOpenFileName(this, + tr("Select scene builder Python script"), + Path::GetEditingGameDataFolder().c_str(), + tr("Python (*.py)")); + + if (pyFilename.isNull()) + { + return; + } + + // reset the script rule from the .assetinfo file if it exists + if (!m_scriptProcessorRuleFilename.empty()) + { + m_scriptProcessorRuleFilename.clear(); + if (QFile::exists(m_assetImporterDocument->GetScene()->GetManifestFilename().c_str())) + { + QFile file(m_assetImporterDocument->GetScene()->GetManifestFilename().c_str()); + file.remove(); + } + } + + // find the path relative to the project folder + pyFilename = Path::GetRelativePath(pyFilename, true); + + // create a script rule + auto scriptProcessorRule = AZStd::make_shared(); + scriptProcessorRule->SetScriptFilename(pyFilename.toUtf8().toStdString().c_str()); + + // add the script rule to the manifest & save off the scene manifest + Containers::SceneManifest sceneManifest; + sceneManifest.AddEntry(scriptProcessorRule); + if (sceneManifest.SaveToFile(m_assetImporterDocument->GetScene()->GetManifestFilename())) + { + OpenFile(m_assetImporterDocument->GetScene()->GetSourceFilename()); + } +} + void AssetImporterWindow::ResetMenuAccess(WindowState state) { if (state == WindowState::FileLoaded) @@ -480,7 +546,7 @@ void AssetImporterWindow::SetTitle(const char* filePath) } AZStd::string fileName; AzFramework::StringFunc::Path::GetFileName(filePath, fileName); - converted->setWindowTitle(QString("%1 Settings (PREVIEW) - %2").arg(extension.c_str(), fileName.c_str())); + converted->setWindowTitle(QString("%1 Settings - %2").arg(extension.c_str(), fileName.c_str())); break; } else @@ -490,6 +556,28 @@ void AssetImporterWindow::SetTitle(const char* filePath) } } +void AssetImporterWindow::UpdateSceneDisplay(const AZStd::shared_ptr scene) const +{ + AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath(); + AZ::IO::FixedMaxPath relativeSourcePath = AZ::IO::PathView(m_fullSourcePath).LexicallyProximate(projectPath); + auto sceneHeaderText = QString::fromUtf8(relativeSourcePath.c_str(), static_cast(relativeSourcePath.Native().size())); + if (!m_scriptProcessorRuleFilename.empty()) + { + sceneHeaderText.append("\n Assigned Python builder script (") + .append(m_scriptProcessorRuleFilename.c_str()) + .append(")"); + } + + if (scene) + { + m_rootDisplay->SetSceneDisplay(sceneHeaderText, scene); + } + else + { + m_rootDisplay->SetSceneHeaderText(sceneHeaderText); + } +} + void AssetImporterWindow::HandleAssetLoadingCompleted() { if (!m_assetImporterDocument->GetScene()) @@ -501,10 +589,24 @@ void AssetImporterWindow::HandleAssetLoadingCompleted() m_fullSourcePath = m_assetImporterDocument->GetScene()->GetSourceFilename(); SetTitle(m_fullSourcePath.c_str()); - AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath(); - AZ::IO::FixedMaxPath relativeSourcePath = AZ::IO::PathView(m_fullSourcePath).LexicallyProximate(projectPath); - auto userFriendlyFileName = QString::fromUtf8(relativeSourcePath.c_str(), static_cast(relativeSourcePath.Native().size())); - m_rootDisplay->SetSceneDisplay(userFriendlyFileName, m_assetImporterDocument->GetScene()); + using namespace AZ::SceneAPI; + m_scriptProcessorRuleFilename.clear(); + + // load up the source scene manifest file + Containers::SceneManifest sceneManifest; + if (sceneManifest.LoadFromFile(m_assetImporterDocument->GetScene()->GetManifestFilename())) + { + // check a Python script rule is in that source manifest + auto view = Containers::MakeDerivedFilterView(sceneManifest.GetValueStorage()); + if (!view.empty()) + { + // record the info about the rule in the class + const auto scriptProcessorRule = &*view.begin(); + m_scriptProcessorRuleFilename = scriptProcessorRule->GetScriptFilename(); + } + } + + UpdateSceneDisplay(m_assetImporterDocument->GetScene()); // Once we've browsed to something successfully, we need to hide the initial browse button layer and // show the main area where all the actual work takes place diff --git a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.h b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.h index 1c62b08c19..b4a46ca1f7 100644 --- a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.h +++ b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.h @@ -47,6 +47,10 @@ namespace AZ { class ProcessingOverlayWidget; } + namespace DataTypes + { + class IScriptProcessorRule; + } } } @@ -80,6 +84,7 @@ public: public slots: void OnSceneResetRequested(); + void OnAssignScript(); void OnOpenDocumentation(); void OnInspect(); @@ -105,6 +110,7 @@ private slots: void OverlayLayerAdded(); void OverlayLayerRemoved(); + void UpdateSceneDisplay(const AZStd::shared_ptr scene = {}) const; private: static const AZ::Uuid s_browseTag; @@ -122,4 +128,6 @@ private: int m_processingOverlayIndex; QSharedPointer m_processingOverlay; + + AZStd::string m_scriptProcessorRuleFilename; }; diff --git a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.ui b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.ui index d773d867e7..e279a7c6b9 100644 --- a/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.ui +++ b/Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.ui @@ -25,7 +25,8 @@ &Edit - + + &Help @@ -49,7 +50,7 @@ QLayout::SetMaximumSize - + @@ -175,7 +176,16 @@ Reset the settings for this file (note: you will have to update to commit) - + + + + Assign Build Script... + + + Assign a Python build script that will override the scene's build rules + + + Documentation @@ -204,6 +214,12 @@ AssetImporterWindow OnSceneResetRequested() + + m_actionAssignScript + triggered() + AssetImporterWindow + OnAssignScript() + m_actionInspect triggered() diff --git a/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.cpp b/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.cpp index 2c00edbc39..359dfcec21 100644 --- a/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.cpp +++ b/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.cpp @@ -44,6 +44,11 @@ AZ::SceneAPI::UI::ManifestWidget* ImporterRootDisplay::GetManifestWidget() return m_manifestWidget.data(); } +void ImporterRootDisplay::SetSceneHeaderText(const QString& headerText) +{ + ui->m_filePathText->setText(headerText); +} + void ImporterRootDisplay::SetSceneDisplay(const QString& headerText, const AZStd::shared_ptr& scene) { AZ_PROFILE_FUNCTION(Editor); @@ -53,7 +58,7 @@ void ImporterRootDisplay::SetSceneDisplay(const QString& headerText, const AZStd return; } - ui->m_filePathText->setText(headerText); + SetSceneHeaderText(headerText); HandleSceneWasReset(scene); diff --git a/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.h b/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.h index 4e088ccb5c..03de717316 100644 --- a/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.h +++ b/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.h @@ -63,6 +63,7 @@ public: AZ::SceneAPI::UI::ManifestWidget* GetManifestWidget(); void SetSceneDisplay(const QString& headerText, const AZStd::shared_ptr& scene); + void SetSceneHeaderText(const QString& headerText); void HandleSceneWasReset(const AZStd::shared_ptr& scene); void HandleSaveWasSuccessful(); bool HasUnsavedChanges() const; diff --git a/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.ui b/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.ui index 2364b41f53..1cad17e19c 100644 --- a/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.ui +++ b/Code/Editor/Plugins/EditorAssetImporter/ImporterRootDisplay.ui @@ -40,7 +40,7 @@ - #m_filePathText { margin: 2px; color: grey; } + #m_filePathText { margin: 2px; color: white; } Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter diff --git a/Code/Editor/Util/3DConnexionDriver.cpp b/Code/Editor/Util/3DConnexionDriver.cpp index 9dc1600185..c0d4393b49 100644 --- a/Code/Editor/Util/3DConnexionDriver.cpp +++ b/Code/Editor/Util/3DConnexionDriver.cpp @@ -116,9 +116,6 @@ bool C3DConnexionDriver::GetInputMessageData(LPARAM lParam, S3DConnexionMessage& { if (event->header.dwType == RIM_TYPEHID) { - static bool bGotTranslation = false, - bGotRotation = false; - static int all6DOFs[6] = {0}; LPRAWHID pRawHid = &event->data.hid; // Translation or Rotation packet? They come in two different packets. diff --git a/Code/Editor/Util/ColorUtils.cpp b/Code/Editor/Util/ColorUtils.cpp deleted file mode 100644 index eaaea8a2ef..0000000000 --- a/Code/Editor/Util/ColorUtils.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 - * - */ - -// Qt -#include -#include - -////////////////////////////////////////////////////////////////////////// -QColor ColorLinearToGamma(ColorF col) -{ - float r = clamp_tpl(col.r, 0.0f, 1.0f); - float g = clamp_tpl(col.g, 0.0f, 1.0f); - float b = clamp_tpl(col.b, 0.0f, 1.0f); - float a = clamp_tpl(col.a, 0.0f, 1.0f); - - r = (float)(r <= 0.0031308 ? (12.92 * r) : (1.055 * pow((double)r, 1.0 / 2.4) - 0.055)); - g = (float)(g <= 0.0031308 ? (12.92 * g) : (1.055 * pow((double)g, 1.0 / 2.4) - 0.055)); - b = (float)(b <= 0.0031308 ? (12.92 * b) : (1.055 * pow((double)b, 1.0 / 2.4) - 0.055)); - - return QColor(int(r * 255.0f), int(g * 255.0f), int(b * 255.0f), int(a * 255.0f)); -} - -////////////////////////////////////////////////////////////////////////// -ColorF ColorGammaToLinear(const QColor& col) -{ - float r = (float)col.red() / 255.0f; - float g = (float)col.green() / 255.0f; - float b = (float)col.blue() / 255.0f; - float a = (float)col.alpha() / 255.0f; - - return ColorF((float)(r <= 0.04045 ? (r / 12.92) : pow(((double)r + 0.055) / 1.055, 2.4)), - (float)(g <= 0.04045 ? (g / 12.92) : pow(((double)g + 0.055) / 1.055, 2.4)), - (float)(b <= 0.04045 ? (b / 12.92) : pow(((double)b + 0.055) / 1.055, 2.4)), a); -} - -QColor ColorToQColor(uint32 color) -{ - return QColor::fromRgbF((float)GetRValue(color) / 255.0f, - (float)GetGValue(color) / 255.0f, - (float)GetBValue(color) / 255.0f); -} diff --git a/Code/Editor/Util/EditorUtils.cpp b/Code/Editor/Util/EditorUtils.cpp index 47264a51ab..440941e469 100644 --- a/Code/Editor/Util/EditorUtils.cpp +++ b/Code/Editor/Util/EditorUtils.cpp @@ -6,6 +6,7 @@ * */ +#include #include "EditorDefs.h" @@ -184,9 +185,9 @@ QColor ColorLinearToGamma(ColorF col) float b = clamp_tpl(col.b, 0.0f, 1.0f); float a = clamp_tpl(col.a, 0.0f, 1.0f); - r = (float)(r <= 0.0031308 ? (12.92 * r) : (1.055 * pow((double)r, 1.0 / 2.4) - 0.055)); - g = (float)(g <= 0.0031308 ? (12.92 * g) : (1.055 * pow((double)g, 1.0 / 2.4) - 0.055)); - b = (float)(b <= 0.0031308 ? (12.92 * b) : (1.055 * pow((double)b, 1.0 / 2.4) - 0.055)); + r = AZ::Color::ConvertSrgbLinearToGamma(r); + g = AZ::Color::ConvertSrgbLinearToGamma(g); + b = AZ::Color::ConvertSrgbLinearToGamma(b); return QColor(int(r * 255.0f), int(g * 255.0f), int(b * 255.0f), int(a * 255.0f)); } @@ -199,9 +200,9 @@ ColorF ColorGammaToLinear(const QColor& col) float b = (float)col.blue() / 255.0f; float a = (float)col.alpha() / 255.0f; - return ColorF((float)(r <= 0.04045 ? (r / 12.92) : pow(((double)r + 0.055) / 1.055, 2.4)), - (float)(g <= 0.04045 ? (g / 12.92) : pow(((double)g + 0.055) / 1.055, 2.4)), - (float)(b <= 0.04045 ? (b / 12.92) : pow(((double)b + 0.055) / 1.055, 2.4)), a); + return ColorF(AZ::Color::ConvertSrgbGammaToLinear(r), + AZ::Color::ConvertSrgbGammaToLinear(g), + AZ::Color::ConvertSrgbGammaToLinear(b), a); } QColor ColorToQColor(uint32 color) diff --git a/Code/Editor/editor_core_files.cmake b/Code/Editor/editor_core_files.cmake index 447d763a63..15ad0f13a1 100644 --- a/Code/Editor/editor_core_files.cmake +++ b/Code/Editor/editor_core_files.cmake @@ -48,7 +48,6 @@ set(FILES Util/Image.cpp Util/ImageHistogram.h Util/Image.h - Util/ColorUtils.cpp Undo/Undo.cpp Undo/IUndoManagerListener.h Undo/IUndoObject.h diff --git a/Code/Framework/AzCore/AzCore/DOM/DomPath.cpp b/Code/Framework/AzCore/AzCore/DOM/DomPath.cpp index 5f6438518f..9be99d5a03 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomPath.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomPath.cpp @@ -7,9 +7,9 @@ */ #include +#include #include #include -#include namespace AZ::Dom { @@ -117,6 +117,36 @@ namespace AZ::Dom return AZStd::get(m_value); } + size_t PathEntry::GetHash() const + { + return AZStd::visit( + [&](auto&& value) -> size_t + { + using CurrentType = AZStd::decay_t; + if constexpr (AZStd::is_same_v) + { + AZStd::hash hasher; + return hasher(value); + } + else if constexpr (AZStd::is_same_v) + { + return value.GetHash(); + } + }, + m_value); + } +} // namespace AZ::Dom + +namespace AZStd +{ + size_t AZStd::hash::operator()(const AZ::Dom::PathEntry& entry) const + { + return entry.GetHash(); + } +} // namespace AZStd + +namespace AZ::Dom +{ const AZ::Name& PathEntry::GetKey() const { AZ_Assert(IsKey(), "Key called on PathEntry that is not a key"); diff --git a/Code/Framework/AzCore/AzCore/DOM/DomPath.h b/Code/Framework/AzCore/AzCore/DOM/DomPath.h index 7a031a2c68..416f3b6903 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomPath.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomPath.h @@ -55,11 +55,24 @@ namespace AZ::Dom size_t GetIndex() const; const AZ::Name& GetKey() const; + size_t GetHash() const; private: AZStd::variant m_value; }; +} // namespace AZ::Dom +namespace AZStd +{ + template<> + struct hash + { + size_t operator()(const AZ::Dom::PathEntry& entry) const; + }; +} // namespace AZStd + +namespace AZ::Dom +{ //! Represents a path, represented as a series of PathEntry values, to a position in a Value. class Path final { @@ -135,7 +148,7 @@ namespace AZ::Dom AZStd::string ToString() const; void AppendToString(AZStd::string& output) const; - template + template void AppendToString(T& output) const { const size_t startIndex = output.length(); diff --git a/Code/Framework/AzCore/AzCore/DOM/DomPrefixTree.h b/Code/Framework/AzCore/AzCore/DOM/DomPrefixTree.h new file mode 100644 index 0000000000..a2d96b5bab --- /dev/null +++ b/Code/Framework/AzCore/AzCore/DOM/DomPrefixTree.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 +#include +#include + +namespace AZ::Dom +{ + //! Specifies how a path matches against a DomPrefixTree + enum class PrefixTreeMatch + { + //! Only an exact path will match. + //! For the path "/foo/bar" only "/foo/bar" will match while + //! "/foo" and "/foo/bar/baz" will not. + ExactPath, + //! The path, and any of its subpaths, will match. + //! For the path "/foo/bar" both "/foo/bar" and any subpaths like + //! "/foo/bar/0" will match, while "/foo" and orthogonal paths like + //! "/bar" will not + PathAndSubpaths, + //! Any of the path's subpaths will match, excepting the path itself. + //! For the path "/foo/bar", "/foo/bar" will not match but "/foo/bar/0" + //! will. + SubpathsOnly, + }; + + template + constexpr bool RangeConvertibleToPrefixTree = false; + + template + constexpr bool RangeConvertibleToPrefixTree< + Range, + T, + AZStd::enable_if_t< + AZStd::ranges::input_range && AZStd::tuple_size>::value == 2 && + AZStd::convertible_to>, Path> && + AZStd::convertible_to>, T>>> = true; + + //! A prefix tree that maps DOM paths to some arbitrary value. + template + class DomPrefixTree + { + public: + DomPrefixTree() = default; + DomPrefixTree(const DomPrefixTree&) = default; + DomPrefixTree(DomPrefixTree&&) = default; + explicit DomPrefixTree(AZStd::initializer_list> init); + + template>> + explicit DomPrefixTree(Range&& range); + + DomPrefixTree& operator=(const DomPrefixTree&) = default; + DomPrefixTree& operator=(DomPrefixTree&&) = default; + + using VisitorFunction = AZStd::function; + + //! Visits a path and calls a visitor for each matching path and value. + void VisitPath(const Path& path, PrefixTreeMatch match, const VisitorFunction& visitor) const; + //! Visits a path and returns the most specific matching value, or null if none was found. + T* ValueAtPath(const Path& path, PrefixTreeMatch match); + //! \see ValueAtPath + const T* ValueAtPath(const Path& path, PrefixTreeMatch match) const; + //! Visits a path and returns the most specific matching value or some default value. + template + T ValueAtPathOrDefault(const Path& path, Deduced&& defaultValue, PrefixTreeMatch match) const; + + //! Sets the value stored at path. + template + void SetValue(const Path& path, Deduced&& value); + //! Removes the value stored at path. If removeChildren is true, also removes any values stored at subpaths. + void EraseValue(const Path& path, bool removedChildren = false); + //! Removes all entries from this tree. + void Clear(); + + private: + struct Node + { + AZStd::unordered_map m_values; + AZStd::optional m_data; + }; + + Node* GetNodeForPath(const Path& path); + const Node* GetNodeForPath(const Path& path) const; + + Node m_rootNode; + }; +} // namespace AZ::Dom + +#include diff --git a/Code/Framework/AzCore/AzCore/DOM/DomPrefixTree.inl b/Code/Framework/AzCore/AzCore/DOM/DomPrefixTree.inl new file mode 100644 index 0000000000..8b37d6bb72 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/DOM/DomPrefixTree.inl @@ -0,0 +1,234 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +namespace AZ::Dom +{ + template + DomPrefixTree::DomPrefixTree(AZStd::initializer_list> init) + { + for (const auto& [path, value] : init) + { + SetValue(path, value); + } + } + + template + template + DomPrefixTree::DomPrefixTree(Range&& range) + { + for (auto&& [path, value] : AZStd::forward(range)) + { + SetValue(path, value); + } + } + + template + auto DomPrefixTree::GetNodeForPath(const Path& path) -> Node* + { + Node* node = &m_rootNode; + for (const auto& entry : path) + { + auto entryIt = node->m_values.find(entry); + if (entryIt == node->m_values.end()) + { + return nullptr; + } + node = &entryIt->second; + } + return node; + } + + template + auto DomPrefixTree::GetNodeForPath(const Path& path) const -> const Node* + { + const Node* node = &m_rootNode; + for (const auto& entry : path) + { + auto entryIt = node->m_values.find(entry); + if (entryIt == node->m_values.end()) + { + return nullptr; + } + node = &entryIt->second; + } + return node; + } + + template + void DomPrefixTree::VisitPath(const Path& path, PrefixTreeMatch match, const VisitorFunction& visitor) const + { + const Node* rootNode = GetNodeForPath(path); + if (rootNode == nullptr) + { + return; + } + + if ((match == PrefixTreeMatch::ExactPath || match == PrefixTreeMatch::PathAndSubpaths) && rootNode->m_data.has_value()) + { + visitor(path, rootNode->m_data.value()); + } + + if (match == PrefixTreeMatch::ExactPath) + { + return; + } + + Path currentPath = path; + struct PopPathEntry + { + }; + using StackEntry = AZStd::variant; + AZStd::stack stack({ rootNode }); + while (!stack.empty()) + { + StackEntry entry = AZStd::move(stack.top()); + stack.pop(); + AZStd::visit( + [&](auto&& value) + { + using CurrentType = AZStd::decay_t; + if constexpr (AZStd::is_same_v) + { + if (value != rootNode && value->m_data.has_value()) + { + visitor(currentPath, value->m_data.value()); + } + + for (const auto& entry : value->m_values) + { + // The stack runs this in reverse order, so we'll: + // 1) Push the current path entry to currentPath + // 2a) Process the value at the path (if any) + // 2b) Process the value's descendants at the path (if any) + // 3) Pop the path entry from the stack + stack.push(PopPathEntry{}); + stack.push(&entry.second); + stack.push(entry.first); + } + } + else if constexpr (AZStd::is_same_v) + { + currentPath.Push(value); + } + else if constexpr (AZStd::is_same_v) + { + currentPath.Pop(); + } + }, + entry); + } + } + + template + T* DomPrefixTree::ValueAtPath(const Path& path, PrefixTreeMatch match) + { + // Just look up the node if we're looking for an exact path + if (match == PrefixTreeMatch::ExactPath) + { + if (Node* node = GetNodeForPath(path); node != nullptr && node->m_data.has_value()) + { + return &node->m_data.value(); + } + return {}; + } + + // Otherwise, walk to find the closest anscestor with a value + Node* node = &m_rootNode; + T* result = nullptr; + const size_t lengthToIterate = match == PrefixTreeMatch::SubpathsOnly ? path.Size() - 1 : path.Size(); + for (size_t i = 0; i < lengthToIterate; ++i) + { + if (node->m_data.has_value()) + { + result = &node->m_data.value(); + } + + const PathEntry& entry = path[i]; + auto entryIt = node->m_values.find(entry); + if (entryIt == node->m_values.end()) + { + break; + } + + node = &entryIt->second; + } + + if (node->m_data.has_value()) + { + result = &node->m_data.value(); + } + + return result; + } + + template + const T* DomPrefixTree::ValueAtPath(const Path& path, PrefixTreeMatch match) const + { + // Const coerce the ValueAtPath result, which doesn't mutate but returns a mutable pointer + return const_cast*>(this)->ValueAtPath(path, match); + } + + template + template + T DomPrefixTree::ValueAtPathOrDefault(const Path& path, Deduced&& defaultValue, PrefixTreeMatch match) const + { + const T* value = ValueAtPath(path, match); + return value == nullptr ? AZStd::forward(defaultValue) : *value; + } + + template + template + void DomPrefixTree::SetValue(const Path& path, Deduced&& value) + { + Node* node = &m_rootNode; + for (const PathEntry& entry : path) + { + // Get or create an entry in this node + node = &node->m_values[entry]; + } + node->m_data = AZStd::forward(value); + } + + template + void DomPrefixTree::EraseValue(const Path& path, bool removeChildren) + { + Node* node = &m_rootNode; + const size_t entriesToIterate = path.Size() - 1; + for (size_t i = 0; i < entriesToIterate; ++i) + { + const PathEntry& entry = path[i]; + auto nodeIt = node->m_values.find(entry); + if (nodeIt == node->m_values.end()) + { + return; + } + node = &nodeIt->second; + } + + auto nodeIt = node->m_values.find(path[path.Size() - 1]); + if (nodeIt != node->m_values.end()) + { + if (removeChildren) + { + node->m_values.erase(nodeIt); + } + else + { + nodeIt->second.m_data = {}; + } + } + } + + template + void DomPrefixTree::Clear() + { + m_rootNode = Node(); + } +} // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 793fadd092..2e266e58a6 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -151,6 +151,18 @@ namespace AZ::Dom return Value(value); } + Value Value::CreateNode(AZ::Name nodeName) + { + Value result(Type::Node); + result.SetNodeName(AZStd::move(nodeName)); + return result; + } + + Value Value::CreateNode(AZStd::string_view nodeName) + { + return CreateNode(AZ::Name(nodeName)); + } + Value::Value(int8_t value) : m_value(aznumeric_cast(value)) { diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index 4620692b5a..f713ed5f71 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -222,6 +222,8 @@ namespace AZ::Dom explicit Value(T*) = delete; static Value FromOpaqueValue(const AZStd::any& value); + static Value CreateNode(AZ::Name nodeName); + static Value CreateNode(AZStd::string_view nodeName); // Equality / comparison / swap... Value& operator=(const Value&); diff --git a/Code/Framework/AzCore/AzCore/Math/Color.h b/Code/Framework/AzCore/AzCore/Math/Color.h index 4f506e219d..c0c798fbb9 100644 --- a/Code/Framework/AzCore/AzCore/Math/Color.h +++ b/Code/Framework/AzCore/AzCore/Math/Color.h @@ -134,6 +134,12 @@ namespace AZ //! Color from u32 => 0xAABBGGRR, RGB convert from Gamma corrected to Linear values. void FromU32GammaToLinear(u32 c); + //! Convert SRGB gamma space to linear space + static float ConvertSrgbGammaToLinear(float x); + + //! Convert SRGB linear space to gamma space + static float ConvertSrgbLinearToGamma(float x); + //! Convert color from linear to gamma corrected space. Color LinearToGamma() const; diff --git a/Code/Framework/AzCore/AzCore/Math/Color.inl b/Code/Framework/AzCore/AzCore/Math/Color.inl index c07d0f2d88..f3b691e163 100644 --- a/Code/Framework/AzCore/AzCore/Math/Color.inl +++ b/Code/Framework/AzCore/AzCore/Math/Color.inl @@ -370,6 +370,15 @@ namespace AZ *this = GammaToLinear(); } + AZ_MATH_INLINE float Color::ConvertSrgbGammaToLinear(float x) + { + return x <= 0.04045 ? (x / 12.92f) : static_cast(pow((static_cast(x) + 0.055) / 1.055, 2.4)); + } + + AZ_MATH_INLINE float Color::ConvertSrgbLinearToGamma(float x) + { + return x <= 0.0031308 ? 12.92f * x : static_cast(1.055 * pow(static_cast(x), 1.0 / 2.4) - 0.055); + } AZ_MATH_INLINE Color Color::LinearToGamma() const { @@ -377,9 +386,9 @@ namespace AZ float g = GetG(); float b = GetB(); - r = (r <= 0.0031308 ? 12.92f * r : static_cast(1.055 * pow(static_cast(r), 1.0 / 2.4) - 0.055)); - g = (g <= 0.0031308 ? 12.92f * g : static_cast(1.055 * pow(static_cast(g), 1.0 / 2.4) - 0.055)); - b = (b <= 0.0031308 ? 12.92f * b : static_cast(1.055 * pow(static_cast(b), 1.0 / 2.4) - 0.055)); + r = ConvertSrgbLinearToGamma(r); + g = ConvertSrgbLinearToGamma(g); + b = ConvertSrgbLinearToGamma(b); return Color(r,g,b,GetA()); } @@ -391,9 +400,9 @@ namespace AZ float g = GetG(); float b = GetB(); - return Color(r <= 0.04045 ? (r / 12.92f) : static_cast(pow((static_cast(r) + 0.055) / 1.055, 2.4)), - g <= 0.04045 ? (g / 12.92f) : static_cast(pow((static_cast(g) + 0.055) / 1.055, 2.4)), - b <= 0.04045 ? (b / 12.92f) : static_cast(pow((static_cast(b) + 0.055) / 1.055, 2.4)), GetA()); + return Color(ConvertSrgbGammaToLinear(r), + ConvertSrgbGammaToLinear(g), + ConvertSrgbGammaToLinear(b), GetA()); } diff --git a/Code/Framework/AzCore/AzCore/Memory/HphaSchema.cpp b/Code/Framework/AzCore/AzCore/Memory/HphaSchema.cpp index 5e0e3e2de8..1b5351cd79 100644 --- a/Code/Framework/AzCore/AzCore/Memory/HphaSchema.cpp +++ b/Code/Framework/AzCore/AzCore/Memory/HphaSchema.cpp @@ -13,6 +13,7 @@ #include // required by certain platforms #include #include +#include #include #ifdef _DEBUG @@ -41,8 +42,8 @@ #define HPPA_ASSERT_PRINT_STACK(...) _EXPAND(_GET_MACRO23(__VA_ARGS__, _HPPA_ASSERT_PRINT_STACK3, _HPPA_ASSERT_PRINT_STACK2)(__VA_ARGS__)) -namespace AZ { - +namespace AZ +{ /// default windows virtual page size \todo Read this from the OS when we create the allocator) #define OS_VIRTUAL_PAGE_SIZE AZ_PAGE_SIZE ////////////////////////////////////////////////////////////////////////// @@ -56,211 +57,78 @@ namespace AZ { // Enabled mutex per bucket #define USE_MUTEX_PER_BUCKET - ////////////////////////////////////////////////////////////////////////// - // TODO: Replace with AZStd::intrusive_list - class intrusive_list_base - { - public: - class node_base - { - node_base* mPrev; - node_base* mNext; - public: - node_base* next() const {return mNext; } - node_base* prev() const {return mPrev; } - void reset() - { - mPrev = this; - mNext = this; - } - void unlink() - { - mNext->mPrev = mPrev; - mPrev->mNext = mNext; - } - void link(node_base* node) - { - mPrev = node->mPrev; - mNext = node; - node->mPrev = this; - mPrev->mNext = this; - } - }; - intrusive_list_base() - { - mHead.reset(); - } - intrusive_list_base(const intrusive_list_base&) - { - mHead.reset(); - } - bool empty() const {return mHead.next() == &mHead; } - void swap(intrusive_list_base& other) - { - node_base* node = &other.mHead; - if (!empty()) - { - node = mHead.next(); - mHead.unlink(); - mHead.reset(); - } - node_base* other_node = &mHead; - if (!other.empty()) - { - other_node = other.mHead.next(); - other.mHead.unlink(); - other.mHead.reset(); - } - mHead.link(other_node); - other.mHead.link(node); - } - protected: - node_base mHead; - }; - - ////////////////////////////////////////////////////////////////////////// - // TODO: Replace with AZStd::intrusive_list - template - class intrusive_list - : public intrusive_list_base - { - intrusive_list(const intrusive_list& rhs); - intrusive_list& operator=(const intrusive_list& rhs); - public: - class node - : public node_base - { - public: - T* next() const {return static_cast(node_base::next()); } - T* prev() const {return static_cast(node_base::prev()); } - const T& data() const {return *static_cast(this); } - T& data() {return *static_cast(this); } - }; - - class const_iterator; - class iterator - { - using reference = T&; - using pointer = T*; - friend class const_iterator; - T* mPtr; - public: - iterator() - : mPtr(0) {} - explicit iterator(T* ptr) - : mPtr(ptr) {} - reference operator*() const {return mPtr->data(); } - pointer operator->() const {return &mPtr->data(); } - operator pointer() const { - return &mPtr->data(); - } - iterator& operator++() - { - mPtr = mPtr->next(); - return *this; - } - iterator& operator--() - { - mPtr = mPtr->prev(); - return *this; - } - bool operator==(const iterator& rhs) const {return mPtr == rhs.mPtr; } - bool operator!=(const iterator& rhs) const {return mPtr != rhs.mPtr; } - T* ptr() const {return mPtr; } - }; - - class const_iterator - { - using reference = const T &; - using pointer = const T *; - const T* mPtr; - public: - const_iterator() - : mPtr(0) {} - explicit const_iterator(const T* ptr) - : mPtr(ptr) {} - const_iterator(const iterator& it) - : mPtr(it.mPtr) {} - reference operator*() const {return mPtr->data(); } - pointer operator->() const {return &mPtr->data(); } - operator pointer() const { - return &mPtr->data(); - } - const_iterator& operator++() - { - mPtr = mPtr->next(); - return *this; - } - const_iterator& operator--() - { - mPtr = mPtr->prev(); - return *this; - } - bool operator==(const const_iterator& rhs) const {return mPtr == rhs.mPtr; } - bool operator!=(const const_iterator& rhs) const {return mPtr != rhs.mPtr; } - const T* ptr() const {return mPtr; } - }; - - intrusive_list() - : intrusive_list_base() {} - ~intrusive_list() {clear(); } - - const_iterator begin() const {return const_iterator((const T*)mHead.next()); } - iterator begin() {return iterator((T*)mHead.next()); } - const_iterator end() const {return const_iterator((const T*)&mHead); } - iterator end() {return iterator((T*)&mHead); } - - const T& front() const - { - HPPA_ASSERT(!empty()); - return *begin(); - } - T& front() - { - HPPA_ASSERT(!empty()); - return *begin(); - } - const T& back() const - { - HPPA_ASSERT(!empty()); - return *(--end()); - } - T& back() - { - HPPA_ASSERT(!empty()); - return *(--end()); - } - - void push_front(T* v) {insert(this->begin(), v); } - void pop_front() {erase(this->begin()); } - void push_back(T* v) {insert(this->end(), v); } - void pop_back() {erase(--(this->end())); } - - iterator insert(iterator where, T* node) - { - T* newLink = node; - newLink->link(where.ptr()); - return iterator(newLink); - } - iterator erase(iterator where) - { - T* node = where.ptr(); - ++where; - node->unlink(); - return where; - } - void erase(T* node) - { - node->unlink(); - } - void clear() - { - while (!this->empty()) - { - this->pop_back(); - } - } - }; + namespace HphaInternal + { + //! Rounds up a value to next power of 2. + //! For example to round 8388609((2^23) + 1) up to 16777216(2^24) the following occurs + //! Subtract one from the value in case it is already + //! equal to a power of 2 + //! 8388609 - 1 = 8388608 + //! Propagate the highest one bit in the value to all the lower bits + //! 8388608 = 0b100'0000'0000'0000'0000'0000 in binary + //! + //! 0b100'0000'0000'0000'0000'0000 + //! |0b010'0000'0000'0000'0000'0000 (>> 1) + //! ------------------------------- + //! 0b110'0000'0000'0000'0000'0000 (Now there are 2 consecutive 1-bits) + //! |0b001'1000'0000'0000'0000'0000 (>> 2) + //! ------------------------------- + //! 0b111'1000'0000'0000'0000'0000 (Now there are 4 consecutive 1-bits) + //! |0b000'0111'1000'0000'0000'0000 (>> 4) + //! ------------------------------- + //! 0b111'1111'1000'0000'0000'0000 (Now there are 8 consecutive 1-bits) + //! |0b000'0000'0111'1111'1000'0000 (>> 8) + //! ------------------------------- + //! 0b111'1111'1111'1111'1000'0000 (Now there are 16 consecutive 1-bits) + //! |0b000'0000'0000'0000'0111'1111 (>> 16) + //! ------------------------------- + //! 0b111'1111'1111'1111'1111'1111 (Now there are 23 consecutive 1-bits) + //! |0b000'0000'0000'0000'0000'0000 (>> 32) + //! ------------------------------- + //! 0b111'1111'1111'1111'1111'1111 + //! Finally since all the one bits are set in the value, adding one pushes it + //! to next power of 2 + //! 0b1000'0000'0000'0000'0000'0000 = 16777216 + static constexpr size_t AlignUpToPowerOfTwo(size_t value) + { + // If the value is <=2 it is already aligned + if (value <= 2) + { + return value; + } + + // Subtract one to make any values already + // aligned to a power of 2 less than that power of 2 + // so that algorithm doesn't push those values upwards + --value; + value |= value >> 0b1; + value |= value >> 0b10; + value |= value >> 0b100; + value |= value >> 0b1000; + value |= value >> 0b1'0000; + value |= value >> 0b10'0000; + ++value; + return value; + } + + static_assert(AlignUpToPowerOfTwo(0) == 0); + static_assert(AlignUpToPowerOfTwo(1) == 1); + static_assert(AlignUpToPowerOfTwo(2) == 2); + static_assert(AlignUpToPowerOfTwo(3) == 4); + static_assert(AlignUpToPowerOfTwo(4) == 4); + static_assert(AlignUpToPowerOfTwo(5) == 8); + static_assert(AlignUpToPowerOfTwo(8) == 8); + static_assert(AlignUpToPowerOfTwo(10) == 16); + static_assert(AlignUpToPowerOfTwo(16) == 16); + static_assert(AlignUpToPowerOfTwo(24) == 32); + static_assert(AlignUpToPowerOfTwo(32) == 32); + static_assert(AlignUpToPowerOfTwo(45) == 64); + static_assert(AlignUpToPowerOfTwo(64) == 64); + static_assert(AlignUpToPowerOfTwo(112) == 128); + static_assert(AlignUpToPowerOfTwo(128) == 128); + static_assert(AlignUpToPowerOfTwo(136) == 256); + static_assert(AlignUpToPowerOfTwo(256) == 256); + } ////////////////////////////////////////////////////////////////////////// class HpAllocator @@ -376,7 +244,7 @@ namespace AZ { }; struct page : public block_header_proxy /* must be first */ - , public intrusive_list::node + , public AZStd::list_base_hook::node_type { page(size_t elemSize, size_t pageSize, size_t marker) : mBucketIndex((unsigned short)bucket_spacing_function_aligned(elemSize)) @@ -415,25 +283,22 @@ namespace AZ { void dec_ref() { HPPA_ASSERT(mUseCount > 0); mUseCount--; } bool check_marker(size_t marker) const { return mMarker == (marker ^ ((size_t)this)); } }; - using page_list = intrusive_list; - class bucket + using page_list = AZStd::intrusive_list>; + +#if defined(MULTITHREADED) && defined(USE_MUTEX_PER_BUCKET) + static constexpr size_t BucketAlignment = HphaInternal::AlignUpToPowerOfTwo(sizeof(page_list) + sizeof(AZStd::mutex) + sizeof(size_t)); +#else + static constexpr size_t BucketAlignment = HphaInternal::AlignUpToPowerOfTwo(sizeof(page_list) + sizeof(size_t)); +#endif + AZ_PUSH_DISABLE_WARNING_MSVC(4324) + class alignas(BucketAlignment) bucket { page_list mPageList; -#ifdef MULTITHREADED - #if defined (USE_MUTEX_PER_BUCKET) +#if defined(MULTITHREADED) && defined(USE_MUTEX_PER_BUCKET) mutable AZStd::mutex mLock; - #endif #endif size_t mMarker; -#ifdef MULTITHREADED - #if defined (USE_MUTEX_PER_BUCKET) - unsigned char _padding[sizeof(void*) * 16 - sizeof(page_list) - sizeof(AZStd::mutex) - sizeof(size_t)]; - #else - unsigned char _padding[sizeof(void*) * 16 - sizeof(page_list) - sizeof(size_t)]; - #endif -#else - unsigned char _padding[sizeof(void*) * 4 - sizeof(page_list) - sizeof(size_t)]; -#endif + public: bucket(); #ifdef MULTITHREADED @@ -442,17 +307,19 @@ namespace AZ { #endif #endif size_t marker() const {return mMarker; } - const page* page_list_begin() const {return mPageList.begin(); } - page* page_list_begin() {return mPageList.begin(); } - const page* page_list_end() const {return mPageList.end(); } - page* page_list_end() {return mPageList.end(); } + auto page_list_begin() const {return mPageList.begin(); } + auto page_list_begin() {return mPageList.begin(); } + auto page_list_end() const {return mPageList.end(); } + auto page_list_end() {return mPageList.end(); } bool page_list_empty() const {return mPageList.empty(); } - void add_free_page(page* p) {mPageList.push_front(p); } + void add_free_page(page* p) {mPageList.push_front(*p); } page* get_free_page(); const page* get_free_page() const; void* alloc(page* p); void free(page* p, void* ptr); + void unlink(page* p); }; + AZ_POP_DISABLE_WARNING_MSVC void* bucket_system_alloc(); void bucket_system_free(void* ptr); page* bucket_grow(size_t elemSize, size_t marker); @@ -1237,8 +1104,6 @@ namespace AZ { // Thats why we use SimpleLcgRandom here AZ::SimpleLcgRandom randGenerator = AZ::SimpleLcgRandom(reinterpret_cast(static_cast(this))); mMarker = size_t(randGenerator.Getu64Random()); - - (void)_padding; } HpAllocator::page* HpAllocator::bucket::get_free_page() @@ -1278,8 +1143,8 @@ namespace AZ { if (!next) { // if full, auto sort to back - p->unlink(); - mPageList.push_back(p); + mPageList.erase(*p); + mPageList.push_back(*p); } return (void*)free; } @@ -1295,11 +1160,16 @@ namespace AZ { if (!free) { // if the page was previously full, auto sort to front - p->unlink(); - mPageList.push_front(p); + mPageList.erase(*p); + mPageList.push_front(*p); } } + void HpAllocator::bucket::unlink(page* p) + { + mPageList.erase(*p); + } + void* HpAllocator::bucket_system_alloc() { void* ptr; @@ -1522,8 +1392,8 @@ namespace AZ { AZStd::lock_guard lock(m_mutex); #endif #endif - const page* pageEnd = mBuckets[i].page_list_end(); - for (const page* p = mBuckets[i].page_list_begin(); p != pageEnd; ) + auto pageEnd = mBuckets[i].page_list_end(); + for (auto p = mBuckets[i].page_list_begin(); p != pageEnd; ) { // early out if we reach fully occupied page (the remaining should all be full) if (p->mFreeList == nullptr) @@ -1537,7 +1407,7 @@ namespace AZ { { AZ_TracePrintf("System", "Unused Bucket %d page %p elementSize: %d available: %d elements\n", i, p, elementSize, availableMemory / elementSize); } - p = p->next(); + p = p->m_next; } } return unusedMemory; @@ -1554,21 +1424,21 @@ namespace AZ { AZStd::lock_guard lock(m_mutex); #endif #endif - page* pageEnd = mBuckets[i].page_list_end(); - for (page* p = mBuckets[i].page_list_begin(); p != pageEnd; ) + auto pageEnd = mBuckets[i].page_list_end(); + for (auto p = mBuckets[i].page_list_begin(); p != pageEnd; ) { // early out if we reach fully occupied page (the remaining should all be full) if (p->mFreeList == nullptr) { break; } - page* next = p->next(); + page* next = p->m_next; if (p->empty()) { HPPA_ASSERT(p->mFreeList); - p->unlink(); + mBuckets[i].unlink(AZStd::to_address(p)); p->setInvalid(); - bucket_system_free(p); + bucket_system_free(AZStd::to_address(p)); } p = next; } @@ -2239,11 +2109,17 @@ namespace AZ { size_t HpAllocator::tree_get_max_allocation() const { +#ifdef MULTITHREADED + AZStd::lock_guard lock(mTreeMutex); +#endif return mFreeTree.maximum()->get_block()->size(); } size_t HpAllocator::tree_get_unused_memory(bool isPrint) const { +#ifdef MULTITHREADED + AZStd::lock_guard lock(mTreeMutex); +#endif size_t unusedMemory = 0; for (free_node_tree::const_iterator it = mFreeTree.begin(); it != mFreeTree.end(); ++it) { @@ -2564,7 +2440,7 @@ namespace AZ { m_capacity = desc.m_capacity; } - AZ_Assert(sizeof(HpAllocator) <= sizeof(m_hpAllocatorBuffer), "Increase the m_hpAllocatorBuffer, we need %d bytes but we have %d bytes!", sizeof(HpAllocator), sizeof(m_hpAllocatorBuffer)); + static_assert(sizeof(HpAllocator) <= sizeof(m_hpAllocatorBuffer), "Increase the m_hpAllocatorBuffer, it needs to be at least the sizeof(HpAllocator)"); m_allocator = new (&m_hpAllocatorBuffer) HpAllocator(m_desc); } diff --git a/Code/Framework/AzCore/AzCore/Memory/HphaSchema.h b/Code/Framework/AzCore/AzCore/Memory/HphaSchema.h index 27dbd321d2..fd746e4602 100644 --- a/Code/Framework/AzCore/AzCore/Memory/HphaSchema.h +++ b/Code/Framework/AzCore/AzCore/Memory/HphaSchema.h @@ -73,18 +73,20 @@ namespace AZ void GarbageCollect() override; private: - // [LY-84974][sconel@][2018-08-10] SliceStrike integration up to CL 671758 // this must be at least the max size of HpAllocator (defined in the cpp) + any platform compiler padding - static const int hpAllocatorStructureSize = 16584; - // [LY][sconel@] end + // A static assert inside of HphaSchema.cpp validates that this is the case + // as of commit https://github.com/o3de/o3de/commit/92cd457c256e1ec91eeabe04b56d1d4c61f8b1af + // When MULTITHREADED and USE_MUTEX_PER_BUCKET is defined + // the largest sizeof for HpAllocator is 16640 on MacOS + // On Windows the sizeof HpAllocator is 8384 + // Up this value to 18 KiB to be safe + static constexpr size_t hpAllocatorStructureSize = 18 * 1024; Descriptor m_desc; int m_pad; // pad the Descriptor to avoid C4355 size_type m_capacity; ///< Capacity in bytes. HpAllocator* m_allocator; - // [LY-84974][sconel@][2018-08-10] SliceStrike integration up to CL 671758 - AZStd::aligned_storage::type m_hpAllocatorBuffer; ///< Memory buffer for HpAllocator - // [LY][sconel@] end + AZStd::aligned_storage_t m_hpAllocatorBuffer; ///< Memory buffer for HpAllocator bool m_ownMemoryBlock; }; } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp index 0ba44e4e61..c08f038c8b 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,8 @@ namespace AZ void JsonSystemComponent::Reflect(ReflectContext* reflectContext) { + JsonConfigurableStackSerializer::Reflect(reflectContext); + if (JsonRegistrationContext* jsonContext = azrtti_cast(reflectContext)) { jsonContext->Serializer()->HandlesType(); diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/RegistrationContext.h b/Code/Framework/AzCore/AzCore/Serialization/Json/RegistrationContext.h index 25a3bfedba..f62dd54e71 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/RegistrationContext.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/RegistrationContext.h @@ -8,11 +8,12 @@ #pragma once +#include +#include #include #include #include #include -#include #include namespace AZ @@ -22,6 +23,7 @@ namespace AZ { public: AZ_RTTI(JsonRegistrationContext, "{5A763774-CA8B-4245-A897-A03C503DCD60}", ReflectContext); + AZ_CLASS_ALLOCATOR(JsonRegistrationContext, SystemAllocator, 0); class SerializerBuilder; using SerializerMap = AZStd::unordered_map, AZStd::hash>; diff --git a/Code/Framework/AzCore/AzCore/Settings/ConfigurableStack.cpp b/Code/Framework/AzCore/AzCore/Settings/ConfigurableStack.cpp new file mode 100644 index 0000000000..320219c7e5 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Settings/ConfigurableStack.cpp @@ -0,0 +1,172 @@ +/* + * 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 +{ + AZ_CLASS_ALLOCATOR_IMPL(JsonConfigurableStackSerializer, AZ::SystemAllocator, 0); + + JsonSerializationResult::Result JsonConfigurableStackSerializer::Load( + void* outputValue, + [[maybe_unused]] const Uuid& outputValueTypeId, + const rapidjson::Value& inputValue, + JsonDeserializerContext& context) + { + namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds. + + switch (inputValue.GetType()) + { + case rapidjson::kArrayType: + return LoadFromArray(outputValue, inputValue, context); + case rapidjson::kObjectType: + return LoadFromObject(outputValue, inputValue, context); + + case rapidjson::kNullType: + [[fallthrough]]; + case rapidjson::kFalseType: + [[fallthrough]]; + case rapidjson::kTrueType: + [[fallthrough]]; + case rapidjson::kStringType: + [[fallthrough]]; + case rapidjson::kNumberType: + return context.Report( + JSR::Tasks::ReadField, JSR::Outcomes::Unsupported, + "Unsupported type. Configurable stack values can only be read from arrays or objects."); + default: + return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown, "Unknown json type encountered for string value."); + } + } + + JsonSerializationResult::Result JsonConfigurableStackSerializer::Store( + [[maybe_unused]] rapidjson::Value& outputValue, + [[maybe_unused]] const void* inputValue, + [[maybe_unused]] const void* defaultValue, + [[maybe_unused]] const Uuid& valueTypeId, + JsonSerializerContext& context) + { + return context.Report( + JsonSerializationResult::Tasks::WriteValue, JsonSerializationResult::Outcomes::Unsupported, + "Configuration stacks can not be written out."); + } + + void JsonConfigurableStackSerializer::Reflect(ReflectContext* context) + { + if (JsonRegistrationContext* jsonContext = azrtti_cast(context)) + { + jsonContext->Serializer()->HandlesType(); + } + } + + JsonSerializationResult::Result JsonConfigurableStackSerializer::LoadFromArray( + void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context) + { + namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds. + + auto stack = reinterpret_cast(outputValue); + const Uuid& nodeValueType = stack->GetNodeType(); + + JSR::ResultCode result(JSR::Tasks::ReadField); + uint32_t counter = 0; + for (auto& it : inputValue.GetArray()) + { + ScopedContextPath subPath(context, counter); + void* value = stack->AddNode(AZStd::to_string(counter)); + result.Combine(ContinueLoading(value, nodeValueType, it, context)); + counter++; + } + + return context.Report( + result, + result.GetProcessing() != JSR::Processing::Halted ? "Loaded configurable stack from array." + : "Failed to load configurable stack from array."); + } + + JsonSerializationResult::Result JsonConfigurableStackSerializer::LoadFromObject( + void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context) + { + namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds. + + auto stack = reinterpret_cast(outputValue); + const Uuid& nodeValueType = stack->GetNodeType(); + + AZStd::queue> + delayedEntries; + JSR::ResultCode result(JSR::Tasks::ReadField); + + auto add = [&](ConfigurableStackInterface::InsertPosition position, rapidjson::Value::ConstMemberIterator target, + rapidjson::Value::ConstMemberIterator it) + { + if (target->value.IsString()) + { + delayedEntries.emplace(position, AZStd::string_view(target->value.GetString(), target->value.GetStringLength()), it); + } + else + { + result.Combine(context.Report( + JSR::Tasks::ReadField, JSR::Outcomes::Skipped, + "Skipped value for the Configurable Stack because the target wasn't a string.")); + } + }; + + // Load all the regular entries into the stack and store any with a before or after binding for + // later inserting. + for (auto it = inputValue.MemberBegin(); it != inputValue.MemberEnd(); ++it) + { + AZStd::string_view name(it->name.GetString(), it->name.GetStringLength()); + ScopedContextPath subPath(context, name); + if (it->value.IsObject()) + { + if (auto target = it->value.FindMember(StackBefore); target != it->value.MemberEnd()) + { + add(ConfigurableStackInterface::InsertPosition::Before, target, it); + continue; + } + if (auto target = it->value.FindMember(StackAfter); target != it->value.MemberEnd()) + { + add(ConfigurableStackInterface::InsertPosition::After, target, it); + continue; + } + } + + void* value = stack->AddNode(name); + result.Combine(ContinueLoading(value, nodeValueType, it->value, context)); + } + + // Insert the entries that have been delayed. + while (!delayedEntries.empty()) + { + auto&& [insertPosition, target, valueStore] = delayedEntries.front(); + AZStd::string_view name(valueStore->name.GetString(), valueStore->name.GetStringLength()); + ScopedContextPath subPath(context, name); + void* value = stack->AddNode(name, target, insertPosition); + if (value != nullptr) + { + result.Combine(ContinueLoading(value, nodeValueType, valueStore->value, context)); + } + else + { + result.Combine(context.Report( + JSR::Tasks::ReadField, JSR::Outcomes::Skipped, + AZStd::string::format( + "Skipped value for the Configurable Stack because the target '%.*s' couldn't be found.", AZ_STRING_ARG(name)))); + } + delayedEntries.pop(); + } + + return context.Report( + result, + result.GetProcessing() != JSR::Processing::Halted ? "Loaded configurable stack from array." + : "Failed to load configurable stack from array."); + } +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Settings/ConfigurableStack.h b/Code/Framework/AzCore/AzCore/Settings/ConfigurableStack.h new file mode 100644 index 0000000000..afb0eca6e0 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Settings/ConfigurableStack.h @@ -0,0 +1,187 @@ +/* + * 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 + +namespace AZ +{ + //! The ConfigurableStack makes configuring stacks and arrays through the Settings Registry easier than using direct serialization + //! to a container like AZStd::vector. It does this by using JSON Objects rather than JSON arrays, although arrays are supported + //! for backwards compatibility. Two key words were added: + //! - $stack_before : Insert the new entry before the referenced entry. Referencing is done by name. + //! - $stack_after : Insert the new entry after the referenced entry. Referencing is done by name. + //! to allow inserting new entries at specific locations. An example of a .setreg file at updates existing settings would be: + //! // Original settings + //! { + //! "Settings in a stack": + //! { + //! "AnOriginalEntry": + //! { + //! "MyValue": "hello", + //! "ExampleValue": 84 + //! }, + //! "TheSecondEntry": + //! { + //! "MyValue": "world" + //! } + //! } + //! } + //! + //! // Customized settings. + //! { + //! "Settings in a stack": + //! { + //! // Add a new entry before "AnOriginalEntry" in the original document. + //! "NewEntry": + //! { + //! "$stack_before": "AnOriginalEntry", + //! "MyValue": 42 + //! }, + //! // Add a second entry after "AnOriginalEntry" in the original document. + //! "SecondNewEntry": + //! { + //! "$stack_after": "AnOriginalEntry", + //! "MyValue": "FortyTwo". + //! }, + //! // Update a value in "AnOriginalEntry". + //! "AnOriginalEntry": + //! { + //! "ExampleValue": 42 + //! }, + //! // Delete the "TheSecondEntry" from the settings. + //! "TheSecondEntry" : null, + //! } + //! } + //! + //! The ConfigurableStack uses an AZStd::shared_ptr to store the values. This supports settings up a base class and specifying + //! derived classes in the settings, but requires that the base and derived classes all have a memory allocator associated with + //! them (i.e. by using the "AZ_CLASS_ALLOCATOR" macro) and that the relation of the classes is reflected. Loading a + //! ConfigurableStack can be done using the GetObject call on the SettingsRegistryInterface. + + class ReflectContext; + + class ConfigurableStackInterface + { + public: + friend class JsonConfigurableStackSerializer; + + virtual ~ConfigurableStackInterface() = default; + + virtual const TypeId& GetNodeType() const = 0; + + protected: + enum class InsertPosition + { + Before, + After + }; + virtual void* AddNode(AZStd::string name) = 0; + virtual void* AddNode(AZStd::string name, AZStd::string_view target, InsertPosition position) = 0; + }; + + template + class ConfigurableStack final : public ConfigurableStackInterface + { + public: + using NodeValue = AZStd::shared_ptr; + using Node = AZStd::pair; + using NodeContainer = AZStd::vector; + using Iterator = typename NodeContainer::iterator; + using ConstIterator = typename NodeContainer::const_iterator; + + ~ConfigurableStack() override = default; + + const TypeId& GetNodeType() const override; + + Iterator begin(); + Iterator end(); + ConstIterator begin() const; + ConstIterator end() const; + + size_t size() const; + + protected: + void* AddNode(AZStd::string name) override; + void* AddNode(AZStd::string name, AZStd::string_view target, InsertPosition position) override; + + private: + NodeContainer m_nodes; + }; + + AZ_TYPE_INFO_TEMPLATE(ConfigurableStack, "{0A3D2038-6E6A-4EFD-A1B4-F30D947E21B1}", AZ_TYPE_INFO_TYPENAME); + + template + struct SerializeGenericTypeInfo> + { + using ConfigurableStackType = ConfigurableStack; + + class GenericConfigurableStackInfo : public GenericClassInfo + { + public: + AZ_TYPE_INFO(GenericConfigurableStackInfo, "{FC5A9353-D0DE-48F6-81B5-1CB2985C5F65}"); + + GenericConfigurableStackInfo(); + + SerializeContext::ClassData* GetClassData() override; + size_t GetNumTemplatedArguments() override; + const Uuid& GetTemplatedTypeId([[maybe_unused]] size_t element) override; + const Uuid& GetSpecializedTypeId() const override; + const Uuid& GetGenericTypeId() const override; + + void Reflect(SerializeContext* serializeContext) override; + + SerializeContext::ClassData m_classData; + }; + + using ClassInfoType = GenericConfigurableStackInfo; + + static ClassInfoType* GetGenericInfo(); + static const Uuid& GetClassTypeId(); + }; + + class JsonConfigurableStackSerializer : public BaseJsonSerializer + { + public: + AZ_RTTI(JsonConfigurableStackSerializer, "{45A31805-9058-41A9-B1A3-71E2CB4D9237}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR_DECL; + + JsonSerializationResult::Result Load( + void* outputValue, + const Uuid& outputValueTypeId, + const rapidjson::Value& inputValue, + JsonDeserializerContext& context) override; + + JsonSerializationResult::Result Store( + rapidjson::Value& outputValue, + const void* inputValue, + const void* defaultValue, + const Uuid& valueTypeId, + JsonSerializerContext& context) override; + + static void Reflect(ReflectContext* context); + + private: + JsonSerializationResult::Result LoadFromArray( + void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context); + JsonSerializationResult::Result LoadFromObject( + void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context); + + static constexpr const char* StackBefore = "$stack_before"; + static constexpr const char* StackAfter = "$stack_after"; + }; +} // namespace AZ + +#include diff --git a/Code/Framework/AzCore/AzCore/Settings/ConfigurableStack.inl b/Code/Framework/AzCore/AzCore/Settings/ConfigurableStack.inl new file mode 100644 index 0000000000..7cfba21497 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Settings/ConfigurableStack.inl @@ -0,0 +1,149 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +namespace AZ +{ + // + // ConfigurableStack + // + + template + const TypeId& ConfigurableStack::GetNodeType() const + { + return azrtti_typeid(); + } + + template + auto ConfigurableStack::begin() -> Iterator + { + return m_nodes.begin(); + } + + template + auto ConfigurableStack::end() -> Iterator + { + return m_nodes.end(); + } + + template + auto ConfigurableStack::begin() const -> ConstIterator + { + return m_nodes.begin(); + } + + template + auto ConfigurableStack::end() const -> ConstIterator + { + return m_nodes.end(); + } + + template + size_t ConfigurableStack::size() const + { + return m_nodes.size(); + } + + template + void* ConfigurableStack::AddNode(AZStd::string name) + { + Node& result = m_nodes.emplace_back(AZStd::move(name)); + return &result.second; + } + + template + void* ConfigurableStack::AddNode(AZStd::string name, AZStd::string_view target, InsertPosition position) + { + auto end = m_nodes.end(); + for (auto it = m_nodes.begin(); it != end; ++it) + { + if (it->first == target) + { + if (position == InsertPosition::After) + { + ++it; + } + auto result = m_nodes.insert(it, Node(AZStd::move(name), {})); + return &result->second; + } + } + return nullptr; + } + + + + // + // SerializeGenericTypeInfo + // + + + template + SerializeGenericTypeInfo>::GenericConfigurableStackInfo::GenericConfigurableStackInfo() + : m_classData{ SerializeContext::ClassData::Create( + "AZ::ConfigurableStack", GetSpecializedTypeId(), Internal::NullFactory::GetInstance(), nullptr, nullptr) } + { + } + + template + SerializeContext::ClassData* SerializeGenericTypeInfo>::GenericConfigurableStackInfo::GetClassData() + { + return &m_classData; + } + + template + size_t SerializeGenericTypeInfo>::GenericConfigurableStackInfo::GetNumTemplatedArguments() + { + return 1; + } + + template + const Uuid& SerializeGenericTypeInfo>::GenericConfigurableStackInfo::GetTemplatedTypeId( + [[maybe_unused]] size_t element) + { + return SerializeGenericTypeInfo::GetClassTypeId(); + } + + template + const Uuid& SerializeGenericTypeInfo>::GenericConfigurableStackInfo::GetSpecializedTypeId() const + { + return azrtti_typeid(); + } + + template + const Uuid& SerializeGenericTypeInfo>::GenericConfigurableStackInfo::GetGenericTypeId() const + { + return TYPEINFO_Uuid(); + } + + template + void SerializeGenericTypeInfo>::GenericConfigurableStackInfo::Reflect(SerializeContext* serializeContext) + { + if (serializeContext) + { + serializeContext->RegisterGenericClassInfo(GetSpecializedTypeId(), this, &AnyTypeInfoConcept::CreateAny); + if (serializeContext->FindClassData(azrtti_typeid>()) == nullptr) + { + serializeContext->RegisterGenericType>(); + } + } + } + + template + auto SerializeGenericTypeInfo>::GetGenericInfo() -> ClassInfoType* + { + return GetCurrentSerializeContextModule().CreateGenericClassInfo(); + } + + template + const Uuid& SerializeGenericTypeInfo>::GetClassTypeId() + { + return GetGenericInfo()->GetClassData()->m_typeId; + } +} // namespace AZ + diff --git a/Code/Framework/AzCore/AzCore/UnitTest/TestTypes.h b/Code/Framework/AzCore/AzCore/UnitTest/TestTypes.h index db8aaadaa1..51838a9626 100644 --- a/Code/Framework/AzCore/AzCore/UnitTest/TestTypes.h +++ b/Code/Framework/AzCore/AzCore/UnitTest/TestTypes.h @@ -159,6 +159,36 @@ namespace UnitTest TeardownAllocator(); } }; + + /** + * RAII wrapper around a BenchmarkEnvironmentBase to encapsulate + * the creation and destuction of the SystemAllocator + * SetUpBenchmark and TearDownBenchmark can still be used for custom + * benchmark environment setup + */ + class ScopedAllocatorBenchmarkEnvironment + : public AZ::Test::BenchmarkEnvironmentBase + { + public: + ScopedAllocatorBenchmarkEnvironment() + { + // Only create the allocator if it doesn't exist + if (!AZ::AllocatorInstance::IsReady()) + { + AZ::AllocatorInstance::Create(); + m_ownsAllocator = true; + } + } + ~ScopedAllocatorBenchmarkEnvironment() override + { + if (m_ownsAllocator) + { + AZ::AllocatorInstance::Destroy(); + } + } + private: + bool m_ownsAllocator{}; + }; #endif class DLLTestVirtualClass diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index b7821f1d65..89923b415f 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -130,6 +130,8 @@ set(FILES DOM/DomVisitor.h DOM/DomComparison.cpp DOM/DomComparison.h + DOM/DomPrefixTree.h + DOM/DomPrefixTree.inl DOM/Backends/JSON/JsonBackend.h DOM/Backends/JSON/JsonSerializationUtils.cpp DOM/Backends/JSON/JsonSerializationUtils.h @@ -560,6 +562,9 @@ set(FILES Serialization/std/VariantReflection.inl Settings/CommandLine.cpp Settings/CommandLine.h + Settings/ConfigurableStack.cpp + Settings/ConfigurableStack.inl + Settings/ConfigurableStack.h Settings/SettingsRegistry.cpp Settings/SettingsRegistry.h Settings/SettingsRegistryConsoleUtils.cpp diff --git a/Code/Framework/AzCore/AzCore/std/containers/intrusive_set.h b/Code/Framework/AzCore/AzCore/std/containers/intrusive_set.h index 3e6dae84d3..4a00d2a081 100644 --- a/Code/Framework/AzCore/AzCore/std/containers/intrusive_set.h +++ b/Code/Framework/AzCore/AzCore/std/containers/intrusive_set.h @@ -42,20 +42,13 @@ namespace AZStd template friend class intrusive_multiset; -#ifdef AZ_DEBUG_BUILD - intrusive_multiset_node() - { - m_children[0] = nullptr; - m_children[1] = nullptr; - m_neighbours[0] = nullptr; - m_neighbours[1] = nullptr; - m_parentColorSide = nullptr; - } + intrusive_multiset_node() = default; ~intrusive_multiset_node() { +#if defined(AZ_DEBUG_BUILD) AZSTD_CONTAINER_ASSERT(m_children[0] == nullptr && m_children[1] == nullptr && m_parentColorSide == nullptr, "AZStd::intrusive_multiset_node - intrusive list node is being destroyed while still in a container!"); - } #endif + } // We use the lower 2 bits of the parent pointer to store enum Bits { @@ -95,9 +88,9 @@ namespace AZStd AZ_FORCE_INLINE T* next() const { return m_neighbours[AZSTD_RBTREE_RIGHT]; } protected: - T* m_children[2]; - T* m_neighbours[2]; - T* m_parentColorSide; + T* m_children[2]{}; + T* m_neighbours[2]{}; + T* m_parentColorSide{}; } AZ_MAY_ALIAS; // we access the object in such way that we potentially can break strict aliasing rules /** @@ -659,12 +652,10 @@ namespace AZStd --m_numElements; -#ifdef AZ_DEBUG_BUILD nodeHook->m_children[0] = nodeHook->m_children[1] = nullptr; nodeHook->m_parentColorSide = nullptr; -#ifdef DEBUG_INTRUSIVE_MULTISET +#if defined(AZ_DEBUG_BUILD) && defined(DEBUG_INTRUSIVE_MULTISET) validate(); -#endif #endif } @@ -955,8 +946,11 @@ namespace AZStd hookNode->setParentSide(side); } + //! pre-condition + //! @param node must be non-nullptr inline static node_ptr_type MinOrMax(const_node_ptr_type node, SideType side) { + AZSTD_CONTAINER_ASSERT(node != nullptr, "MinOrMax can only be called with a non-nullptr node") const_node_ptr_type cur = node; const_node_ptr_type minMax = cur; while (!iterator_impl::isNil(cur)) diff --git a/Code/Framework/AzCore/AzCore/std/ranges/ranges_adaptor.h b/Code/Framework/AzCore/AzCore/std/ranges/ranges_adaptor.h index 6c814def5d..10e8998584 100644 --- a/Code/Framework/AzCore/AzCore/std/ranges/ranges_adaptor.h +++ b/Code/Framework/AzCore/AzCore/std/ranges/ranges_adaptor.h @@ -23,28 +23,28 @@ namespace AZStd::ranges::views::Internal {} template constexpr decltype(auto) operator()(Args&&... args) & - noexcept(noexcept(AZStd::invoke(m_outer, AZStd::invoke(m_inner, AZStd::forward(args)...)))) + noexcept(noexcept(AZStd::invoke(declval(), AZStd::invoke(declval(), AZStd::forward(args)...)))) { return AZStd::invoke(m_outer, AZStd::invoke(m_inner, AZStd::forward(args)...)); } template constexpr decltype(auto) operator()(Args&&... args) const& - noexcept(noexcept(AZStd::invoke(m_outer, AZStd::invoke(m_inner, AZStd::forward(args)...)))) + noexcept(noexcept(AZStd::invoke(declval(), AZStd::invoke(declval(), AZStd::forward(args)...)))) { return AZStd::invoke(m_outer, AZStd::invoke(m_inner, AZStd::forward(args)...)); } // template constexpr decltype(auto) operator()(Args&&... args) && - noexcept(noexcept(AZStd::invoke(AZStd::move(m_outer), - AZStd::invoke(AZStd::move(m_inner), AZStd::forward(args)...)))) + noexcept(noexcept(AZStd::invoke(declval(), + AZStd::invoke(declval(), AZStd::forward(args)...)))) { return AZStd::invoke(AZStd::move(m_outer), AZStd::invoke(AZStd::move(m_inner), AZStd::forward(args)...)); } template constexpr decltype(auto) operator()(Args&&... args) const&& - noexcept(noexcept(AZStd::invoke(AZStd::move(m_outer), - AZStd::invoke(AZStd::move(m_inner), AZStd::forward(args)...)))) + noexcept(noexcept(AZStd::invoke(declval(), + AZStd::invoke(declval(), AZStd::forward(args)...)))) { return AZStd::invoke(AZStd::move(m_outer), AZStd::invoke(AZStd::move(m_inner), AZStd::forward(args)...)); } diff --git a/Code/Framework/AzCore/AzCore/std/string/string_view.h b/Code/Framework/AzCore/AzCore/std/string/string_view.h index af1482b05b..e9becea4b0 100644 --- a/Code/Framework/AzCore/AzCore/std/string/string_view.h +++ b/Code/Framework/AzCore/AzCore/std/string/string_view.h @@ -595,31 +595,44 @@ namespace AZStd static constexpr int_type not_eof(int_type c) noexcept { return c != eof() ? c : !eof(); } }; + // string_view forward declaation + template > + class basic_string_view; +} + +namespace AZStd::Internal +{ + template + struct has_operator_basic_string_view + : false_type + {}; + + template + struct has_operator_basic_string_view::operator basic_string_view)>> + : true_type + {}; + + // If the range has a traits_type element, it must match + template + static constexpr bool range_trait_type_matches = true; + template + inline constexpr bool range_trait_type_matches::traits_type>, + bool_constant::traits_type, Traits>> + >>> = false; +} +namespace AZStd +{ /** - * Immutable string wrapper based on boost::const_string and std::string_view. When we operate on + * Immutable string wrapper based on std::string_view. When we operate on * const char* we don't know if this points to NULL terminated string or just a char array. * to have a clear distinction between them we provide this wrapper. */ - template > + template class basic_string_view { - template - static constexpr bool has_operator_basic_string_view = false; - template - static constexpr bool has_operator_basic_string_view>().operator basic_string_view()) - >> = true; - - // If the range has a traits_type element, it must match - template - static constexpr bool range_trait_type_matches = true; - template - static constexpr bool range_trait_type_matches::traits_type>, - bool_constant::traits_type, Traits>> - >>> = false; - public: using traits_type = Traits; using value_type = Element; @@ -674,8 +687,8 @@ namespace AZStd bool_constant>, bool_constant>, bool_constant, value_type>>, - bool_constant>, - bool_constant> + negation>, + bool_constant> >>> constexpr basic_string_view(R&& r) : m_begin(ranges::data(r)) diff --git a/Code/Framework/AzCore/Platform/Windows/AzCore/Debug/StackTracer_Windows.cpp b/Code/Framework/AzCore/Platform/Windows/AzCore/Debug/StackTracer_Windows.cpp index 90362a3ce8..0d3cc7e032 100644 --- a/Code/Framework/AzCore/Platform/Windows/AzCore/Debug/StackTracer_Windows.cpp +++ b/Code/Framework/AzCore/Platform/Windows/AzCore/Debug/StackTracer_Windows.cpp @@ -140,7 +140,7 @@ namespace AZ { SymRegisterCallback64_t g_SymRegisterCallback64; HMODULE g_dbgHelpDll; -#define LOAD_FUNCTION(A) { g_##A = (A##_t)GetProcAddress(g_dbgHelpDll, #A); AZ_Assert(g_##A != 0, ("Can not load %s function!",#A)); } +#define LOAD_FUNCTION(A) { g_##A = (A##_t)GetProcAddress(g_dbgHelpDll, #A); AZ_Assert(g_##A != 0, "Can not load %s function!",#A); } using namespace AZ::Debug; @@ -596,7 +596,7 @@ namespace AZ { result = GetLastError(); } ULONGLONG fileVersion = 0; - if (szImg != NULL) + if (szImg[0]) // !szImg.empty() { // try to retrieve the file-version: VS_FIXEDFILEINFO* fInfo = NULL; @@ -624,43 +624,6 @@ namespace AZ { } } - // Retrive some additional-infos about the module - IMAGEHLP_MODULE64 Module; - const char* szSymType = "-unknown-"; - if (GetModuleInfo(hProcess, baseAddr, &Module) != FALSE) - { - switch (Module.SymType) - { - case SymNone: - szSymType = "-nosymbols-"; - break; - case SymCoff: - szSymType = "COFF"; - break; - case SymCv: - szSymType = "CV"; - break; - case SymPdb: - szSymType = "PDB"; - break; - case SymExport: - szSymType = "-exported-"; - break; - case SymDeferred: - szSymType = "-deferred-"; - break; - case SymSym: - szSymType = "SYM"; - break; - case 8: //SymVirtual: - szSymType = "Virtual"; - break; - case 9: // SymDia: - szSymType = "DIA"; - break; - } - } - // find insert position if (g_moduleInfo.size() < g_moduleInfo.capacity()) { @@ -1073,7 +1036,7 @@ cleanup: } HANDLE hThread = nativeThread; - CONTEXT alignas(8) context; // Without this alignment the function randomly crashes in release. + alignas(alignof(CONTEXT)) CONTEXT context; // Without this alignment the function randomly crashes in release. context.ContextFlags = CONTEXT_ALL; GetThreadContext(hThread, &context); diff --git a/Code/Framework/AzCore/Tests/AZStd/Examples.cpp b/Code/Framework/AzCore/Tests/AZStd/Examples.cpp index 518eb5eed6..89441b711e 100644 --- a/Code/Framework/AzCore/Tests/AZStd/Examples.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/Examples.cpp @@ -443,7 +443,7 @@ namespace UnitTest : m_data(data) { /* expensive operations */ } private: - int m_data; + [[maybe_unused]] int m_data; }; ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzCore/Tests/AZStd/String.cpp b/Code/Framework/AzCore/Tests/AZStd/String.cpp index 50450a898c..f53a90047f 100644 --- a/Code/Framework/AzCore/Tests/AZStd/String.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/String.cpp @@ -2537,7 +2537,7 @@ namespace Benchmark { AZStd::string test1{ "foo bar"}; AZStd::string test2{ "bar foo" }; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { SwapStringViaPointerSizedSwaps(test1, test2); } @@ -2547,7 +2547,7 @@ namespace Benchmark { AZStd::string test1{ "The brown quick wolf jumped over the hyperactive cat" }; AZStd::string test2{ "The quick brown fox jumped over the lazy dog" }; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { SwapStringViaPointerSizedSwaps(test1, test2); } @@ -2557,7 +2557,7 @@ namespace Benchmark { AZStd::string test1{ "foo bar" }; AZStd::string test2{ "bar foo" }; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { SwapStringViaMemcpy(test1, test2); } @@ -2567,7 +2567,7 @@ namespace Benchmark { AZStd::string test1{ "The brown quick wolf jumped over the hyperactive cat" }; AZStd::string test2{ "The quick brown fox jumped over the lazy dog" }; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { SwapStringViaMemcpy(test1, test2); } @@ -2584,7 +2584,7 @@ namespace Benchmark AZStd::string sourceString(state.range(0), 'a'); const char* sourceAddress = sourceString.c_str(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::string assignString; assignString.assign(sourceAddress); @@ -2600,7 +2600,7 @@ namespace Benchmark const char* sourceAddress = sourceString.c_str(); const size_t sourceSize = sourceString.size(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::string assignString; assignString.assign(sourceAddress, sourceSize); @@ -2616,7 +2616,7 @@ namespace Benchmark auto sourceBegin = sourceString.begin(); auto sourceEnd = sourceString.end(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::string assignString; assignString.assign(sourceBegin, sourceEnd); @@ -2631,7 +2631,7 @@ namespace Benchmark AZStd::string sourceString(state.range(0), 'a'); AZStd::string_view sourceView(sourceString); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::string assignString; assignString.assign(sourceView); @@ -2645,7 +2645,7 @@ namespace Benchmark { AZStd::string sourceString(state.range(0), 'a'); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::string assignString; assignString.assign(sourceString); @@ -2659,7 +2659,7 @@ namespace Benchmark { AZStd::string sourceString(state.range(0), 'a'); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::string assignString; assignString.assign(AZStd::move(sourceString)); @@ -2671,7 +2671,7 @@ namespace Benchmark BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_StringAssignFromSingleCharacter, AZStd::string)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::string assignString; assignString.assign(state.range(0), 'a'); @@ -2689,7 +2689,7 @@ namespace Benchmark AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); const char* sourceAddress = sourceString.c_str(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::fixed_string<1024> assignString; assignString.assign(sourceAddress); @@ -2705,7 +2705,7 @@ namespace Benchmark const char* sourceAddress = sourceString.c_str(); const size_t sourceSize = sourceString.size(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::fixed_string<1024> assignString; assignString.assign(sourceAddress, sourceSize); @@ -2721,7 +2721,7 @@ namespace Benchmark auto sourceBegin = sourceString.begin(); auto sourceEnd = sourceString.end(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::fixed_string<1024> assignString; assignString.assign(sourceBegin, sourceEnd); @@ -2736,7 +2736,7 @@ namespace Benchmark AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); AZStd::string_view sourceView(sourceString); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::fixed_string<1024> assignString; assignString.assign(sourceView); @@ -2750,7 +2750,7 @@ namespace Benchmark { AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::fixed_string<1024> assignString; assignString.assign(sourceString); @@ -2764,7 +2764,7 @@ namespace Benchmark { AZStd::fixed_string<1024> sourceString(state.range(0), 'a'); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::fixed_string<1024> assignString; assignString.assign(AZStd::move(sourceString)); @@ -2776,7 +2776,7 @@ namespace Benchmark BENCHMARK_TEMPLATE_DEFINE_F(StringTemplateBenchmarkFixture, BM_FixedStringAssignFromSingleCharacter, AZStd::fixed_string<1024>)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::fixed_string<1024> assignString; assignString.assign(state.range(0), 'a'); diff --git a/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp index f84b60af07..65bd609e37 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomJsonBenchmarks.cpp @@ -29,7 +29,7 @@ namespace AZ::Dom::Benchmark AZ::Dom::JsonBackend backend; AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); AZStd::string payloadCopy = serializedPayload; @@ -53,7 +53,7 @@ namespace AZ::Dom::Benchmark AZ::Dom::JsonBackend backend; AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); AZStd::string payloadCopy = serializedPayload; @@ -77,7 +77,7 @@ namespace AZ::Dom::Benchmark AZ::Dom::JsonBackend backend; AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { auto result = AZ::Dom::Json::WriteToRapidJsonDocument( [&](AZ::Dom::Visitor& visitor) @@ -97,7 +97,7 @@ namespace AZ::Dom::Benchmark AZ::Dom::JsonBackend backend; AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { auto result = AZ::Dom::Utils::WriteToValue( [&](AZ::Dom::Visitor& visitor) @@ -117,7 +117,7 @@ namespace AZ::Dom::Benchmark AZ::Dom::JsonBackend backend; AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { auto result = AZ::JsonSerializationUtils::ReadJsonString(serializedPayload); @@ -130,7 +130,7 @@ namespace AZ::Dom::Benchmark BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonMakeComplexObject)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { TakeAndDiscardWithoutTimingDtor(GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)), state); } @@ -152,7 +152,7 @@ namespace AZ::Dom::Benchmark document.GetAllocator()); } - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (const AZStd::string& key : keys) { @@ -168,7 +168,7 @@ namespace AZ::Dom::Benchmark { rapidjson::Document original = GenerateDomJsonBenchmarkDocument(state.range(0), state.range(1)); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { rapidjson::Document copy; copy.CopyFrom(original, copy.GetAllocator(), true); @@ -184,7 +184,7 @@ namespace AZ::Dom::Benchmark { rapidjson::Document original = GenerateDomJsonBenchmarkDocument(state.range(0), state.range(1)); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { rapidjson::Document copy; copy.CopyFrom(original, copy.GetAllocator(), true); diff --git a/Code/Framework/AzCore/Tests/DOM/DomPatchBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomPatchBenchmarks.cpp index e44ba3ce67..d67597255f 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomPatchBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomPatchBenchmarks.cpp @@ -78,7 +78,7 @@ namespace AZ::Dom::Benchmark if (apply) { auto patchInfo = GenerateHierarchicalDeltaPatch(m_before, m_after); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { auto patchResult = patchInfo.m_forwardPatches.Apply(m_before); benchmark::DoNotOptimize(patchResult); @@ -86,7 +86,7 @@ namespace AZ::Dom::Benchmark } else { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { auto patchInfo = GenerateHierarchicalDeltaPatch(m_before, m_after); benchmark::DoNotOptimize(patchInfo); diff --git a/Code/Framework/AzCore/Tests/DOM/DomPathBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomPathBenchmarks.cpp index 4741900aa1..56d1e1f6ab 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomPathBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomPathBenchmarks.cpp @@ -20,7 +20,7 @@ namespace AZ::Dom::Benchmark PathEntry end; end.SetEndOfArray(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { Path p; p /= entry1; @@ -40,7 +40,7 @@ namespace AZ::Dom::Benchmark PathEntry end; end.SetEndOfArray(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { Path p = Path() / entry1 / entry2 / 0 / end; } @@ -55,7 +55,7 @@ namespace AZ::Dom::Benchmark AZStd::string s; s.resize_no_construct(p.GetStringLength()); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { p.GetStringLength(); p.FormatString(s.data(), s.size()); @@ -69,7 +69,7 @@ namespace AZ::Dom::Benchmark { AZStd::string pathString = "/path/with/multiple/0/different/components/including-long-strings-like-this/65536/999/-"; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { Path p(pathString); benchmark::DoNotOptimize(p); @@ -86,7 +86,7 @@ namespace AZ::Dom::Benchmark PathEntry endOfArray; endOfArray.SetEndOfArray(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { name.IsEndOfArray(); index.IsEndOfArray(); @@ -104,7 +104,7 @@ namespace AZ::Dom::Benchmark PathEntry endOfArray; endOfArray.SetEndOfArray(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { benchmark::DoNotOptimize(name == name); benchmark::DoNotOptimize(name == index); diff --git a/Code/Framework/AzCore/Tests/DOM/DomPrefixTreeBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomPrefixTreeBenchmarks.cpp new file mode 100644 index 0000000000..db55109835 --- /dev/null +++ b/Code/Framework/AzCore/Tests/DOM/DomPrefixTreeBenchmarks.cpp @@ -0,0 +1,104 @@ +/* + * 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 + +#define REGISTER_TREE_BENCHMARK(BaseClass, Method) \ + BENCHMARK_REGISTER_F(BaseClass, Method)->Args({10, 1})->Args({1000, 1})->Args({10, 5})->Args({1000, 5}) + +namespace AZ::Dom::Benchmark +{ + class DomPrefixTreeBenchmark : public Tests::DomBenchmarkFixture + { + public: + void SetUpHarness() override + { + Tests::DomBenchmarkFixture::SetUpHarness(); + m_registeredPaths = AZStd::make_unique>(); + } + + void TearDownHarness() override + { + m_registeredPaths.reset(); + m_tree.Clear(); + Tests::DomBenchmarkFixture::TearDownHarness(); + } + + void SetupTree(benchmark::State& state) + { + const size_t numPaths = aznumeric_cast(state.range(0)); + const size_t depth = aznumeric_cast(state.range(1)); + + Path path("/root"); + for (size_t i = 0; i < numPaths; ++i) + { + for (size_t c = 0; c < depth; ++c) + { + path.Push(i % 4); + m_tree.SetValue(path, AZStd::string::format("entry%zu", i)); + m_registeredPaths->push_back(path); + } + + for (size_t c = 0; c < depth; ++c) + { + path.Pop(); + } + } + } + + DomPrefixTree m_tree; + AZStd::unique_ptr> m_registeredPaths; + }; + + BENCHMARK_DEFINE_F(DomPrefixTreeBenchmark, FindValue_ExactPath)(benchmark::State& state) + { + SetupTree(state); + for (auto _ : state) + { + for (const auto& pathToCheck : *m_registeredPaths) + { + benchmark::DoNotOptimize(m_tree.ValueAtPath(pathToCheck, PrefixTreeMatch::ExactPath)); + } + } + state.SetItemsProcessed(m_registeredPaths->size() * state.iterations()); + } + REGISTER_TREE_BENCHMARK(DomPrefixTreeBenchmark, FindValue_ExactPath); + + BENCHMARK_DEFINE_F(DomPrefixTreeBenchmark, FindValue_InexactPath)(benchmark::State& state) + { + SetupTree(state); + for (auto _ : state) + { + for (const auto& pathToCheck : *m_registeredPaths) + { + benchmark::DoNotOptimize(m_tree.ValueAtPath(pathToCheck, PrefixTreeMatch::PathAndSubpaths)); + } + } + state.SetItemsProcessed(m_registeredPaths->size() * state.iterations()); + } + REGISTER_TREE_BENCHMARK(DomPrefixTreeBenchmark, FindValue_InexactPath); + + BENCHMARK_DEFINE_F(DomPrefixTreeBenchmark, FindValue_VisitEntries)(benchmark::State& state) + { + SetupTree(state); + + for (auto _ : state) + { + m_tree.VisitPath(Path(), PrefixTreeMatch::PathAndSubpaths, [](const Path& path, const AZStd::string& value) + { + benchmark::DoNotOptimize(path); + benchmark::DoNotOptimize(value); + }); + } + state.SetItemsProcessed(m_registeredPaths->size() * state.iterations()); + } + REGISTER_TREE_BENCHMARK(DomPrefixTreeBenchmark, FindValue_VisitEntries); +} + +#undef REGISTER_TREE_BENCHMARK diff --git a/Code/Framework/AzCore/Tests/DOM/DomPrefixTreeTests.cpp b/Code/Framework/AzCore/Tests/DOM/DomPrefixTreeTests.cpp new file mode 100644 index 0000000000..be9c7db0c2 --- /dev/null +++ b/Code/Framework/AzCore/Tests/DOM/DomPrefixTreeTests.cpp @@ -0,0 +1,204 @@ +/* + * 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 AZ::Dom::Tests +{ + using DomPrefixTreeTests = DomTestFixture; + + static_assert(!RangeConvertibleToPrefixTree, int>, "Non-pair range should not convert to tree"); + static_assert( + !RangeConvertibleToPrefixTree>, int>, + "Mismatched value type should not convert to tree"); + static_assert( + !RangeConvertibleToPrefixTree>, int>, + "Mismatched value type should not convert to tree"); + static_assert( + RangeConvertibleToPrefixTree>, int>, + "Vector with path / key type pairs should convert to tree"); + + TEST_F(DomPrefixTreeTests, InitializeFromInitializerList) + { + DomPrefixTree tree({ + { Path(), 0 }, + { Path("/foo/bar"), 1 }, + }); + + EXPECT_EQ(0, *tree.ValueAtPath(Path(), PrefixTreeMatch::ExactPath)); + EXPECT_EQ(1, *tree.ValueAtPath(Path("/foo/bar"), PrefixTreeMatch::ExactPath)); + } + + TEST_F(DomPrefixTreeTests, InitializeFromRange) + { + AZStd::vector> container({ + { Path(), 21 }, + { Path("/foo/bar"), 42 }, + }); + DomPrefixTree tree(container); + + EXPECT_EQ(21, *tree.ValueAtPath(Path(), PrefixTreeMatch::ExactPath)); + EXPECT_EQ(42, *tree.ValueAtPath(Path("/foo/bar"), PrefixTreeMatch::ExactPath)); + } + + TEST_F(DomPrefixTreeTests, GetAndSetRoot) + { + DomPrefixTree tree; + tree.SetValue(Path(), "root"); + EXPECT_EQ("root", *tree.ValueAtPath(Path(), PrefixTreeMatch::ExactPath)); + } + + TEST_F(DomPrefixTreeTests, GetExactPath) + { + DomPrefixTree tree; + + tree.SetValue(Path("/foo/0"), 0); + tree.SetValue(Path("/foo/1"), 42); + tree.SetValue(Path("/foo/foo"), 1); + tree.SetValue(Path("/foo/bar"), 2); + + EXPECT_EQ(0, *tree.ValueAtPath(Path("/foo/0"), PrefixTreeMatch::ExactPath)); + EXPECT_EQ(42, *tree.ValueAtPath(Path("/foo/1"), PrefixTreeMatch::ExactPath)); + EXPECT_EQ(1, *tree.ValueAtPath(Path("/foo/foo"), PrefixTreeMatch::ExactPath)); + EXPECT_EQ(2, *tree.ValueAtPath(Path("/foo/bar"), PrefixTreeMatch::ExactPath)); + + EXPECT_EQ(nullptr, tree.ValueAtPath(Path(), PrefixTreeMatch::ExactPath)); + EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/foo"), PrefixTreeMatch::ExactPath)); + EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/foo/0/subpath"), PrefixTreeMatch::ExactPath)); + } + + TEST_F(DomPrefixTreeTests, GetSubpath) + { + DomPrefixTree tree; + + tree.SetValue(Path("/foo/0"), 0); + tree.SetValue(Path("/foo/1"), 42); + + EXPECT_EQ(0, *tree.ValueAtPath(Path("/foo/0/bar"), PrefixTreeMatch::SubpathsOnly)); + EXPECT_EQ(0, *tree.ValueAtPath(Path("/foo/0/bar/baz"), PrefixTreeMatch::SubpathsOnly)); + EXPECT_EQ(42, *tree.ValueAtPath(Path("/foo/1/0"), PrefixTreeMatch::SubpathsOnly)); + + EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/foo/0"), PrefixTreeMatch::SubpathsOnly)); + EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/foo/1"), PrefixTreeMatch::SubpathsOnly)); + } + + TEST_F(DomPrefixTreeTests, GetPathOrSubpath) + { + DomPrefixTree tree; + + tree.SetValue(Path("/foo/0"), 0); + tree.SetValue(Path("/foo/1"), 42); + + EXPECT_EQ(0, *tree.ValueAtPath(Path("/foo/0"), PrefixTreeMatch::PathAndSubpaths)); + EXPECT_EQ(0, *tree.ValueAtPath(Path("/foo/0/bar"), PrefixTreeMatch::PathAndSubpaths)); + EXPECT_EQ(0, *tree.ValueAtPath(Path("/foo/0/bar/baz"), PrefixTreeMatch::PathAndSubpaths)); + EXPECT_EQ(42, *tree.ValueAtPath(Path("/foo/1"), PrefixTreeMatch::PathAndSubpaths)); + EXPECT_EQ(42, *tree.ValueAtPath(Path("/foo/1/0"), PrefixTreeMatch::PathAndSubpaths)); + + EXPECT_EQ(nullptr, tree.ValueAtPath(Path(), PrefixTreeMatch::PathAndSubpaths)); + EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/foo"), PrefixTreeMatch::PathAndSubpaths)); + EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/path/0"), PrefixTreeMatch::PathAndSubpaths)); + } + + TEST_F(DomPrefixTreeTests, RemovePath) + { + DomPrefixTree tree; + + tree.SetValue(Path(), 20); + tree.SetValue(Path("/foo"), 40); + tree.SetValue(Path("/foo/0"), 80); + + tree.EraseValue(Path("/foo")); + + EXPECT_EQ(20, *tree.ValueAtPath(Path("/foo"), PrefixTreeMatch::PathAndSubpaths)); + EXPECT_EQ(80, *tree.ValueAtPath(Path("/foo/0"), PrefixTreeMatch::PathAndSubpaths)); + } + + TEST_F(DomPrefixTreeTests, RemovePathAndChildren) + { + DomPrefixTree tree; + + tree.SetValue(Path(), 20); + tree.SetValue(Path("/foo"), 40); + tree.SetValue(Path("/foo/0"), 80); + + tree.EraseValue(Path("/foo"), true); + + EXPECT_EQ(20, *tree.ValueAtPath(Path("/foo"), PrefixTreeMatch::PathAndSubpaths)); + EXPECT_EQ(20, *tree.ValueAtPath(Path("/foo/0"), PrefixTreeMatch::PathAndSubpaths)); + } + + TEST_F(DomPrefixTreeTests, ClearTree) + { + DomPrefixTree tree; + + tree.SetValue(Path(), 20); + tree.SetValue(Path("/foo"), 40); + + tree.Clear(); + + EXPECT_EQ(-10, tree.ValueAtPathOrDefault(Path("/foo"), -10, PrefixTreeMatch::PathAndSubpaths)); + } + + TEST_F(DomPrefixTreeTests, Visit) + { + DomPrefixTree tree; + + AZStd::vector> results; + auto visitorFn = [&results](const Path& path, int n) + { + results.emplace_back(path, n); + }; + + auto validateResult = [&results](const Path& path, int n) + { + for (const auto& pair : results) + { + if (pair.first == path) + { + return pair.second == n; + } + } + return false; + }; + + tree.SetValue(Path("/foo"), 99); + tree.SetValue(Path("/foo/0"), 0); + tree.SetValue(Path("/foo/1"), 42); + tree.SetValue(Path("/bar/bat"), 1); + tree.SetValue(Path("/bar/baz"), 2); + + tree.VisitPath(Path("/bar"), PrefixTreeMatch::ExactPath, visitorFn); + EXPECT_EQ(0, results.size()); + results.clear(); + + tree.VisitPath(Path("/foo/0"), PrefixTreeMatch::ExactPath, visitorFn); + EXPECT_EQ(1, results.size()); + EXPECT_TRUE(validateResult(Path("/foo/0"), 0)); + results.clear(); + + tree.VisitPath(Path("/foo/1"), PrefixTreeMatch::ExactPath, visitorFn); + EXPECT_EQ(1, results.size()); + EXPECT_TRUE(validateResult(Path("/foo/1"), 42)); + results.clear(); + + tree.VisitPath(Path("/foo"), PrefixTreeMatch::SubpathsOnly, visitorFn); + EXPECT_EQ(2, results.size()); + EXPECT_TRUE(validateResult(Path("/foo/0"), 0)); + EXPECT_TRUE(validateResult(Path("/foo/1"), 42)); + results.clear(); + + tree.VisitPath(Path("/foo"), PrefixTreeMatch::PathAndSubpaths, visitorFn); + EXPECT_EQ(3, results.size()); + EXPECT_TRUE(validateResult(Path("/foo"), 99)); + EXPECT_TRUE(validateResult(Path("/foo/0"), 0)); + EXPECT_TRUE(validateResult(Path("/foo/1"), 42)); + results.clear(); + } +} // namespace AZ::Dom::Tests diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp index 69d99eb12b..b73306f516 100644 --- a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp @@ -29,7 +29,7 @@ namespace AZ::Dom::Benchmark Value doubleValue(4.0); Value stringValue("foo", true); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { (intValue.GetType()); (boolValue.GetType()); @@ -118,7 +118,7 @@ namespace AZ::Dom::Benchmark value.GetInternalValue()); }; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { (getTypeViaVisit(intValue)); (getTypeViaVisit(boolValue)); @@ -136,7 +136,7 @@ namespace AZ::Dom::Benchmark BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueMakeComplexObject)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { TakeAndDiscardWithoutTimingDtor(GenerateDomBenchmarkPayload(state.range(0), state.range(1)), state); } @@ -149,7 +149,7 @@ namespace AZ::Dom::Benchmark { Value original = GenerateDomBenchmarkPayload(state.range(0), state.range(1)); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { Value copy = original; benchmark::DoNotOptimize(copy); @@ -163,7 +163,7 @@ namespace AZ::Dom::Benchmark { Value original = GenerateDomBenchmarkPayload(state.range(0), state.range(1)); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { Value copy = original; copy["entries"]["Key0"].ArrayPushBack(Value(42)); @@ -178,7 +178,7 @@ namespace AZ::Dom::Benchmark { Value original = GenerateDomBenchmarkPayload(state.range(0), state.range(1)); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { Value copy = Utils::DeepCopy(original); TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state); @@ -199,7 +199,7 @@ namespace AZ::Dom::Benchmark value[key] = i; } - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (const AZ::Name& key : keys) { @@ -222,7 +222,7 @@ namespace AZ::Dom::Benchmark value[key] = i; } - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (const AZStd::string& key : keys) { @@ -245,7 +245,7 @@ namespace AZ::Dom::Benchmark value[key] = i; } - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (const AZStd::string& key : keys) { diff --git a/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp b/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp index cf239a0821..91620ae918 100644 --- a/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp +++ b/Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp @@ -966,7 +966,7 @@ namespace Benchmark BENCHMARK_F(PathBenchmarkFixture, BM_PathAppendFixedPath)(benchmark::State& state) { AZ::IO::FixedMaxPath m_testPath{ "." }; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (const auto& appendPath : m_appendPaths) { @@ -977,7 +977,7 @@ namespace Benchmark BENCHMARK_F(PathBenchmarkFixture, BM_PathAppendAllocatingPath)(benchmark::State& state) { AZ::IO::Path m_testPath{ "." }; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (const auto& appendPath : m_appendPaths) { @@ -989,7 +989,7 @@ namespace Benchmark BENCHMARK_F(PathBenchmarkFixture, BM_StringFuncPathJoinFixedString)(benchmark::State& state) { AZStd::string m_testPath{ "." }; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (const auto& appendPath : m_appendPaths) { @@ -1000,7 +1000,7 @@ namespace Benchmark BENCHMARK_F(PathBenchmarkFixture, BM_StringFuncPathJoinAZStdString)(benchmark::State& state) { AZStd::string m_testPath{ "." }; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (const auto& appendPath : m_appendPaths) { diff --git a/Code/Framework/AzCore/Tests/Jobs.cpp b/Code/Framework/AzCore/Tests/Jobs.cpp index 20c960535d..afe2d72acf 100644 --- a/Code/Framework/AzCore/Tests/Jobs.cpp +++ b/Code/Framework/AzCore/Tests/Jobs.cpp @@ -1741,7 +1741,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunSmallNumberOfLightWeightJobsWithDefaultPriority)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithDefaultPriority(SMALL_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1749,7 +1749,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunMediumNumberOfLightWeightJobsWithDefaultPriority)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithDefaultPriority(MEDIUM_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1757,7 +1757,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunLargeNumberOfLightWeightJobsWithDefaultPriority)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithDefaultPriority(LARGE_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1765,7 +1765,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunSmallNumberOfMediumWeightJobsWithDefaultPriority)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithDefaultPriority(SMALL_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1773,7 +1773,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunMediumNumberOfMediumWeightJobsWithDefaultPriority)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithDefaultPriority(MEDIUM_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1781,7 +1781,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunLargeNumberOfMediumWeightJobsWithDefaultPriority)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithDefaultPriority(LARGE_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1789,7 +1789,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunSmallNumberOfHeavyWeightJobsWithDefaultPriority)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithDefaultPriority(SMALL_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1797,7 +1797,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunMediumNumberOfHeavyWeightJobsWithDefaultPriority)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithDefaultPriority(MEDIUM_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1805,7 +1805,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunLargeNumberOfHeavyWeightJobsWithDefaultPriority)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithDefaultPriority(LARGE_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1813,7 +1813,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunSmallNumberOfRandomWeightJobsWithDefaultPriority)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(SMALL_NUMBER_OF_JOBS); } @@ -1821,7 +1821,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunMediumNumberOfRandomWeightJobsWithDefaultPriority)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(MEDIUM_NUMBER_OF_JOBS); } @@ -1829,7 +1829,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunLargeNumberOfRandomWeightJobsWithDefaultPriority)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(LARGE_NUMBER_OF_JOBS); } @@ -1837,7 +1837,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunSmallNumberOfLightWeightJobsWithRandomPriorities)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomPriority(SMALL_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1845,7 +1845,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunMediumNumberOfLightWeightJobsWithRandomPriorities)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomPriority(MEDIUM_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1853,7 +1853,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunLargeNumberOfLightWeightJobsWithRandomPriorities)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomPriority(LARGE_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1861,7 +1861,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunSmallNumberOfMediumWeightJobsWithRandomPriorities)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomPriority(SMALL_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1869,7 +1869,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunMediumNumberOfMediumWeightJobsWithRandomPriorities)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomPriority(MEDIUM_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1877,7 +1877,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunLargeNumberOfMediumWeightJobsWithRandomPriorities)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomPriority(LARGE_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1885,7 +1885,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunSmallNumberOfHeavyWeightJobsWithRandomPriorities)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomPriority(SMALL_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1893,7 +1893,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunMediumNumberOfHeavyWeightJobsWithRandomPriorities)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomPriority(MEDIUM_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1901,7 +1901,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunLargeNumberOfHeavyWeightJobsWithRandomPriorities)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomPriority(LARGE_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); } @@ -1909,7 +1909,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunSmallNumberOfRandomWeightJobsWithRandomPriorities)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(SMALL_NUMBER_OF_JOBS); } @@ -1917,7 +1917,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunMediumNumberOfRandomWeightJobsWithRandomPriorities)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(MEDIUM_NUMBER_OF_JOBS); } @@ -1925,7 +1925,7 @@ namespace Benchmark BENCHMARK_F(JobBenchmarkFixture, RunLargeNumberOfRandomWeightJobsWithRandomPriorities)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(LARGE_NUMBER_OF_JOBS); } diff --git a/Code/Framework/AzCore/Tests/Main.cpp b/Code/Framework/AzCore/Tests/Main.cpp index 3458f85546..ee23d4ceab 100644 --- a/Code/Framework/AzCore/Tests/Main.cpp +++ b/Code/Framework/AzCore/Tests/Main.cpp @@ -7,29 +7,13 @@ */ #include +#include #include -#include #if defined(HAVE_BENCHMARK) -namespace AzCore -{ - class AzCoreBenchmarkEnvironment - : public AZ::Test::BenchmarkEnvironmentBase - { - void SetUpBenchmark() override - { - AZ::AllocatorInstance::Create(); - } - void TearDownBenchmark() override - { - AZ::AllocatorInstance::Destroy(); - } - }; -} - -AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV, AzCore::AzCoreBenchmarkEnvironment) +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV, UnitTest::ScopedAllocatorBenchmarkEnvironment) #else diff --git a/Code/Framework/AzCore/Tests/Math/CrcTests.cpp b/Code/Framework/AzCore/Tests/Math/CrcTests.cpp index 0255c1ad6d..f152744486 100644 --- a/Code/Framework/AzCore/Tests/Math/CrcTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/CrcTests.cpp @@ -54,7 +54,7 @@ namespace Benchmark { // Runtime performance is not actually being measured by this test. // This function only exist to calculate AZ::Crc32 values at compile time - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { [[maybe_unused]] constexpr auto resultArray = Crc32Internal::GenerateTestCrc32Values(); } diff --git a/Code/Framework/AzCore/Tests/Math/FrustumPerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/FrustumPerformanceTests.cpp index e26d896e5a..7e753fffb9 100644 --- a/Code/Framework/AzCore/Tests/Math/FrustumPerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/FrustumPerformanceTests.cpp @@ -63,7 +63,7 @@ namespace Benchmark BENCHMARK_F(BM_MathFrustum, SphereIntersect)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& data : m_dataArray) { @@ -75,7 +75,7 @@ namespace Benchmark BENCHMARK_F(BM_MathFrustum, AabbIntersect)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& data : m_dataArray) { diff --git a/Code/Framework/AzCore/Tests/Math/Matrix3x3PerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/Matrix3x3PerformanceTests.cpp index 918673d475..c6fe9c8178 100644 --- a/Code/Framework/AzCore/Tests/Math/Matrix3x3PerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/Matrix3x3PerformanceTests.cpp @@ -68,7 +68,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, CreateIdentity)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -80,7 +80,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, CreateZero)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -92,7 +92,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, GetRowX3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -108,7 +108,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, GetColumnX3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -124,7 +124,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, CreateFromValue)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -136,7 +136,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, CreateFromRowMajorFloat9)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -148,7 +148,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, CreateFromColumnMajorFloat9)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -162,7 +162,7 @@ namespace Benchmark { float storeValues[9]; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -176,7 +176,7 @@ namespace Benchmark { float storeValues[9]; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -188,7 +188,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, CreateRotationX)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -200,7 +200,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, CreateRotationY)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -212,7 +212,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, CreateRotationZ)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -224,7 +224,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, CreateFromTransform)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -236,7 +236,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, CreateFromMatrix4x4)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -248,7 +248,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, CreateFromQuaternion)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -260,7 +260,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, CreateScale)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -272,7 +272,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, CreateDiagonal)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -284,7 +284,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, CreateCrossProduct)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -296,7 +296,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, GetElement)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -308,7 +308,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, SetElement)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -321,7 +321,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, SetRowX3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -336,7 +336,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, SetColumnX3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -351,7 +351,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, GetBasisX)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -363,7 +363,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, GetBasisY)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -375,7 +375,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, GetBasisZ)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -387,7 +387,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, OperatorAssign)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -399,7 +399,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, OperatorMultiply)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -411,7 +411,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, TransposedMultiply)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -423,7 +423,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, MultiplyVector)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -435,7 +435,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, OperatorSum)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -447,7 +447,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, OperatorDifference)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -459,7 +459,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, OperatorMultiplyScalar)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -471,7 +471,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, OperatorDivideScalar)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -483,7 +483,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, Transpose)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -494,7 +494,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, GetTranspose)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -506,7 +506,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, RetrieveScale)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -518,7 +518,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, ExtractScale)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -530,7 +530,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, MultiplyByScaleX2)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -542,7 +542,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, GetPolarDecomposition)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -557,7 +557,7 @@ namespace Benchmark AZ::Matrix3x3 orthogonalOut; AZ::Matrix3x3 symmetricOut; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -568,7 +568,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, GetInverseFast)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -580,7 +580,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, GetInverseFull)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -592,7 +592,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, Orthogonalize)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -603,7 +603,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, GetOrthogonalized)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -615,7 +615,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, IsOrthogonal)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -627,7 +627,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, GetDiagonal)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -639,7 +639,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, GetDeterminant)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -651,7 +651,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x3, GetAdjugate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { diff --git a/Code/Framework/AzCore/Tests/Math/Matrix3x4PerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/Matrix3x4PerformanceTests.cpp index 63ddefdd2c..fd942056a2 100644 --- a/Code/Framework/AzCore/Tests/Math/Matrix3x4PerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/Matrix3x4PerformanceTests.cpp @@ -86,7 +86,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateIdentity)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -98,7 +98,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateZero)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -110,7 +110,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateFromValue)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -122,7 +122,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateFromRowMajorFloat12)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -134,7 +134,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateFromRows)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -146,7 +146,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateFromColumnMajorFloat12)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -158,7 +158,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateFromColumns)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -170,7 +170,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateFromColumnMajorFloat16)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -182,7 +182,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateRotationX)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -194,7 +194,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateRotationY)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -206,7 +206,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateRotationZ)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -218,7 +218,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateFromQuaternion)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -230,7 +230,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateFromQuaternionAndTranslation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -242,7 +242,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateFromMatrix3x3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -254,7 +254,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateFromMatrix3x3AndTranslation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -266,7 +266,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateFromTransform)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -278,7 +278,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateScale)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -290,7 +290,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateFromTranslation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -302,7 +302,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, CreateLookAt)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -316,7 +316,7 @@ namespace Benchmark { float testFloats[12]; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -330,7 +330,7 @@ namespace Benchmark { float testFloats[12]; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -344,7 +344,7 @@ namespace Benchmark { float testFloats[16]; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -356,7 +356,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetElement)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -368,7 +368,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetElement)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -381,7 +381,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetRow)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -393,7 +393,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetRowAsVector3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -405,7 +405,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetRowWithFloats)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -418,7 +418,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetRowWithVector3AndFloat)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -431,7 +431,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetRowWithVector4)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -444,7 +444,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetRows)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -459,7 +459,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetRows)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -472,7 +472,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetColumn)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -484,7 +484,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetColumnWithFloats)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -497,7 +497,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetColumnWithVector3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -510,7 +510,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetColumns)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -526,7 +526,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetColumns)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -539,7 +539,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetBasisX)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -551,7 +551,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetBasisXWithFloats)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -564,7 +564,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetBasisXWithVector3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -577,7 +577,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetBasisY)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -589,7 +589,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetBasisYWithFloats)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -602,7 +602,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetBasisYWithVector3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -615,7 +615,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetBasisZ)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -627,7 +627,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetBasisZWithFloats)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -640,7 +640,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetBasisZWithVector3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -653,7 +653,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetTranslation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -665,7 +665,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetTranslationWithFloats)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -678,7 +678,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetTranslationWithVector3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -691,7 +691,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetBasisAndTranslation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -707,7 +707,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetBasisAndTranslation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -720,7 +720,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, OperatorMultiplyMatrix3x4)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -732,7 +732,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, OperatorMultiplyEqualsMatrix3x4)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -745,7 +745,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, TransformPointVector3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -757,7 +757,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, TransformVectorVector4)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -769,7 +769,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, Multiply3x3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -781,7 +781,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetTranspose)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -793,7 +793,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, Transpose)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -806,7 +806,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetTranspose3x3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -818,7 +818,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, Transpose3x3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -831,7 +831,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetInverseFull)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -843,7 +843,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, InvertFull)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -856,7 +856,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetInverseFast)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -868,7 +868,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, InvertFast)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -881,7 +881,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, RetrieveScale)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -893,7 +893,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, ExtractScale)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -906,7 +906,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, MultiplyByScale)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -919,7 +919,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, IsOrthogonal)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -931,7 +931,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetOrthogonalized)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -943,7 +943,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, Orthogonalize)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -956,7 +956,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, IsCloseExactAndDifferent)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -971,7 +971,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, OperatorEqualEqual)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -983,7 +983,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, OperatorNotEqual)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -995,7 +995,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetEulerDegrees)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -1007,7 +1007,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetEulerRadians)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -1019,7 +1019,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetFromEulerDegrees)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -1032,7 +1032,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetFromEulerRadians)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -1045,7 +1045,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, SetRotationPartFromQuaternion)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -1058,7 +1058,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, GetDeterminant3x3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -1070,7 +1070,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix3x4, IsFinite)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { diff --git a/Code/Framework/AzCore/Tests/Math/Matrix4x4PerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/Matrix4x4PerformanceTests.cpp index 21f440c3a1..49a433a7fc 100644 --- a/Code/Framework/AzCore/Tests/Math/Matrix4x4PerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/Matrix4x4PerformanceTests.cpp @@ -64,7 +64,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateIdentity)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -76,7 +76,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateZero)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -88,7 +88,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, GetRowX4)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -106,7 +106,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, GetColumnX3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -124,7 +124,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateFromValue)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -136,7 +136,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateFromRowMajorFloat16)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -148,7 +148,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateFromColumnMajorFloat9)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -160,7 +160,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateProjection)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -172,7 +172,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateProjectionFov)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -184,7 +184,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateInterpolated)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -198,7 +198,7 @@ namespace Benchmark { float storeValues[16]; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -212,7 +212,7 @@ namespace Benchmark { float storeValues[16]; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -224,7 +224,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateRotationX)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -236,7 +236,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateRotationY)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -248,7 +248,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateRotationZ)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -260,7 +260,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateFromQuaternion)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -272,7 +272,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateScale)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -284,7 +284,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateDiagonal)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -296,7 +296,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, GetElement)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -308,7 +308,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, SetElement)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -321,7 +321,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, SetRowX3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -337,7 +337,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, SetColumnX3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -353,7 +353,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, GetBasisX)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -365,7 +365,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, GetBasisY)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -377,7 +377,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, GetBasisZ)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -389,7 +389,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, CreateTranslation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -401,7 +401,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, SetTranslation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -414,7 +414,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, GetTranslation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -426,7 +426,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, OperatorAssign)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -438,7 +438,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, OperatorMultiply)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -450,7 +450,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, OperatorMultiplyAssign)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -463,7 +463,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, OperatorMultiplyVector3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -475,7 +475,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, OperatorMultiplyVector4)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -487,7 +487,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, TransposedMultiply3x3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -499,7 +499,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, Multiply3x3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -511,7 +511,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, Transpose)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -524,7 +524,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, GetTranspose)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -536,7 +536,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, GetInverseFast)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -552,7 +552,7 @@ namespace Benchmark AZ::Matrix4x4 mat = AZ::Matrix4x4::CreateRotationX(1.0f); mat.SetElement(0, 1, 23.1234f); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -564,7 +564,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, GetInverseFull)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -576,7 +576,7 @@ namespace Benchmark BENCHMARK_F(BM_MathMatrix4x4, SetRotationPartFromQuaternion)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { diff --git a/Code/Framework/AzCore/Tests/Math/ObbPerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/ObbPerformanceTests.cpp index d1e8ac2225..ac95912035 100644 --- a/Code/Framework/AzCore/Tests/Math/ObbPerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/ObbPerformanceTests.cpp @@ -47,7 +47,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, CreateFromPositionRotationAndHalfLengths)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -60,7 +60,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, SetPosition)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -72,7 +72,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, GetPosition)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -84,7 +84,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, GetAxisX)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -96,7 +96,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, GetAxisY)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -108,7 +108,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, GetAxisZ)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -120,7 +120,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, GetAxisIndex3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -136,7 +136,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, SetHalfLengthX)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -148,7 +148,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, GetHalfLengthX)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -160,7 +160,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, SetHalfLengthY)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -172,7 +172,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, GetHalfLengthY)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -184,7 +184,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, SetHalfLengthZ)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -196,7 +196,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, GetHalfLengthZ)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -208,7 +208,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, SetHalfLengthIndex3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -222,7 +222,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, GetHalfLengthIndex3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -242,7 +242,7 @@ namespace Benchmark AZ::Vector3 max(120.0f, 300.0f, 50.0f); AZ::Aabb aabb = AZ::Aabb::CreateFromMinMax(min, max); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -256,7 +256,7 @@ namespace Benchmark { AZ::Transform transform = AZ::Transform::CreateRotationY(AZ::DegToRad(90.0f)); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -268,7 +268,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, Equal)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { @@ -280,7 +280,7 @@ namespace Benchmark BENCHMARK_F(BM_MathObb, IsFinite)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < numIters; ++i) { diff --git a/Code/Framework/AzCore/Tests/Math/PlanePerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/PlanePerformanceTests.cpp index e066207635..919dc72988 100644 --- a/Code/Framework/AzCore/Tests/Math/PlanePerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/PlanePerformanceTests.cpp @@ -73,7 +73,7 @@ namespace Benchmark BENCHMARK_F(BM_MathPlane, CreateFromNormalAndDistance)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -85,7 +85,7 @@ namespace Benchmark BENCHMARK_F(BM_MathPlane, GetDistance)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -97,7 +97,7 @@ namespace Benchmark BENCHMARK_F(BM_MathPlane, GetNormal)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -109,7 +109,7 @@ namespace Benchmark BENCHMARK_F(BM_MathPlane, CreateFromNormalAndPoint)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -128,7 +128,7 @@ namespace Benchmark coeff.push_back(unif(rng)); } - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -148,7 +148,7 @@ namespace Benchmark coeff.push_back(unif(rng)); } - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -160,7 +160,7 @@ namespace Benchmark BENCHMARK_F(BM_MathPlane, SetVector3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -179,7 +179,7 @@ namespace Benchmark vecs.push_back(AZ::Vector4(unif(rng), unif(rng), unif(rng), unif(rng))); } - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -192,7 +192,7 @@ namespace Benchmark BENCHMARK_F(BM_MathPlane, SetNormal)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -205,7 +205,7 @@ namespace Benchmark BENCHMARK_F(BM_MathPlane, SetDistance)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -218,7 +218,7 @@ namespace Benchmark BENCHMARK_F(BM_MathPlane, GetPlaneEquationCoefficients)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -236,7 +236,7 @@ namespace Benchmark trans.push_back(AZ::Transform::CreateRotationY(AZ::DegToRad(unif(rng)))); } - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -254,7 +254,7 @@ namespace Benchmark trans.push_back(AZ::Transform::CreateRotationY(AZ::DegToRad(unif(rng)))); } - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -265,7 +265,7 @@ namespace Benchmark BENCHMARK_F(BM_MathPlane, GetPointDist)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -277,7 +277,7 @@ namespace Benchmark BENCHMARK_F(BM_MathPlane, GetProjected)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -291,7 +291,7 @@ namespace Benchmark { AZ::Vector3 rayResult; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -305,7 +305,7 @@ namespace Benchmark { float rayResult; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { @@ -319,7 +319,7 @@ namespace Benchmark { AZ::Vector3 rayResult; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters - 1; ++i) { @@ -333,7 +333,7 @@ namespace Benchmark { float rayResult; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters - 1; ++i) { @@ -345,7 +345,7 @@ namespace Benchmark BENCHMARK_F(BM_MathPlane, IsFinite)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (int i = 0; i < m_numIters; ++i) { diff --git a/Code/Framework/AzCore/Tests/Math/QuaternionPerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/QuaternionPerformanceTests.cpp index 486a33ba67..4ef42c3016 100644 --- a/Code/Framework/AzCore/Tests/Math/QuaternionPerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/QuaternionPerformanceTests.cpp @@ -68,7 +68,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, SplatFloatConstruction)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -80,7 +80,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, NonNormalized4FloatsConstruction)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -92,7 +92,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, CreateFromIdentity)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& quatData : m_quatDataArray) { @@ -104,7 +104,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, CreateZero)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& quatData : m_quatDataArray) { @@ -116,7 +116,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, CreateRotationX)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -128,7 +128,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, CreateRotationY)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -140,7 +140,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, CreateRotationZ)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -155,7 +155,7 @@ namespace Benchmark const AZ::Vector3 vec1 = AZ::Vector3(1.0f, 2.0f, 3.0f).GetNormalized(); const AZ::Vector3 vec2 = AZ::Vector3(-2.0f, 7.0f, -1.0f).GetNormalized(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& quatData : m_quatDataArray) { @@ -170,7 +170,7 @@ namespace Benchmark const AZ::Vector3 vec1 = AZ::Vector3(1.0f, 2.0f, 3.0f).GetNormalized(); const AZ::Vector3 vec2 = AZ::Vector3(-1.0f, -2.0f, -3.0f).GetNormalized(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& quatData : m_quatDataArray) { @@ -182,7 +182,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetX)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -194,7 +194,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetY)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -206,7 +206,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetZ)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -218,7 +218,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetW)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -230,7 +230,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, SetX)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -243,7 +243,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, SetY)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -256,7 +256,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, SetZ)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -269,7 +269,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, SetW)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -282,7 +282,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, SetSplat)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -295,7 +295,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, SetAll)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -310,7 +310,7 @@ namespace Benchmark { AZ::Vector3 vec(5.0f, 6.0f, 7.0f); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& quatData : m_quatDataArray) { @@ -324,7 +324,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, SetArray)(benchmark::State& state) { const float quatArray[4] = { 5.0f, 6.0f, 7.0f, 8.0f }; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& quatData : m_quatDataArray) { @@ -337,7 +337,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, SetElements)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -353,7 +353,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetElement)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -374,7 +374,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetConjugate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -386,7 +386,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetInverseFast)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -398,7 +398,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetInverseFull)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -410,7 +410,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, Dot)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -422,7 +422,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetLength)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -434,7 +434,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetLengthEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -446,7 +446,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetLengthReciprocal)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -458,7 +458,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetLengthReciprocalEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -470,7 +470,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetNormalized)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -482,7 +482,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetNormalizedEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -494,7 +494,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, NormalizeWithLength)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -506,7 +506,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, NormalizeWithLengthEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -518,7 +518,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, Lerp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -530,7 +530,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, NLerp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -542,7 +542,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, Slerp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -554,7 +554,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, OperatorEquality)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -566,7 +566,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, OperatorInequality)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -578,7 +578,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, OperatorNegate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -590,7 +590,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, OperatorSum)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -602,7 +602,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, OperatorSub)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -614,7 +614,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, OperatorMultiply)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -626,7 +626,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, OperatorMultiplyScalar)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -638,7 +638,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, TransformVector)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -653,7 +653,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, IsClose)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -665,7 +665,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, IsIdentity)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -677,7 +677,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetEulerDegrees)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -689,7 +689,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, GetEulerRadians)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -701,7 +701,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, SetFromEulerRadians)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -715,7 +715,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, SetFromEulerDegrees)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -729,7 +729,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, ConvertToAxisAngle)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& quatData : m_quatDataArray) { @@ -744,7 +744,7 @@ namespace Benchmark BENCHMARK_F(BM_MathQuaternion, AggregateMultiply)(::benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZ::Vector3 position = AZ::Vector3::CreateZero(); for (auto& quatData : m_quatDataArray) diff --git a/Code/Framework/AzCore/Tests/Math/ShapeIntersectionPerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/ShapeIntersectionPerformanceTests.cpp index ecab1717b2..7526bdb778 100644 --- a/Code/Framework/AzCore/Tests/Math/ShapeIntersectionPerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/ShapeIntersectionPerformanceTests.cpp @@ -80,7 +80,7 @@ namespace Benchmark BENCHMARK_F(BM_MathShapeIntersection, ContainsFrustumPoint)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -103,7 +103,7 @@ namespace Benchmark BENCHMARK_F(BM_MathShapeIntersection, OverlapsFrustumSphere)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -120,7 +120,7 @@ namespace Benchmark BENCHMARK_F(BM_MathShapeIntersection, ContainsFrustumSphere)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -137,7 +137,7 @@ namespace Benchmark BENCHMARK_F(BM_MathShapeIntersection, OverlapsFrustumAabb)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -154,7 +154,7 @@ namespace Benchmark BENCHMARK_F(BM_MathShapeIntersection, ContainsFrustumAabb)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { diff --git a/Code/Framework/AzCore/Tests/Math/TransformPerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/TransformPerformanceTests.cpp index dde0192ef9..37d9a32820 100644 --- a/Code/Framework/AzCore/Tests/Math/TransformPerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/TransformPerformanceTests.cpp @@ -78,7 +78,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, CreateIdentity)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for ([[maybe_unused]] auto& testData : m_testDataArray) { @@ -90,7 +90,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, CreateRotationX)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -102,7 +102,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, CreateRotationY)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -114,7 +114,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, CreateRotationZ)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -126,7 +126,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, CreateFromQuaternion)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -138,7 +138,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, CreateFromQuaternionAndTranslation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -150,7 +150,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, CreateFromMatrix3x3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -162,7 +162,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, CreateFromMatrix3x3AndTranslation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -174,7 +174,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, CreateFromMatrix3x4)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -186,7 +186,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, CreateUniformScale)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -198,7 +198,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, CreateFromTranslation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -210,7 +210,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, CreateLookAt)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -223,7 +223,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, GetBasis)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -235,7 +235,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, GetBasisX)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -247,7 +247,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, GetBasisY)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -259,7 +259,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, GetBasisZ)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -271,7 +271,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, GetBasisAndTranslation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -287,7 +287,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, GetTranslation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -299,7 +299,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, SetTranslationWithFloats)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -312,7 +312,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, SetTranslationWithVector3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -325,7 +325,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, GetRotation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -337,7 +337,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, SetRotation)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -350,7 +350,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, GetUniformScale)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -362,7 +362,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, SetUniformScale)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -375,7 +375,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, ExtractUniformScale)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -388,7 +388,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, OperatorMultiplyTransform)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -400,7 +400,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, OperatorMultiplyEqualsTransform)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -413,7 +413,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, TransformPointVector3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -425,7 +425,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, TransformPointVector4)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -437,7 +437,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, TransformVector)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -449,7 +449,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, GetInverse)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -461,7 +461,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, Invert)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -474,7 +474,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, IsOrthogonal)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -486,7 +486,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, GetOrthogonalized)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -498,7 +498,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, Orthogonalize)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -511,7 +511,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, IsCloseExactAndDifferent)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -526,7 +526,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, OperatorEqualEqual)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -538,7 +538,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, OperatorNotEqual)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -550,7 +550,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, GetEulerDegrees)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -562,7 +562,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, GetEulerRadians)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -574,7 +574,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, SetFromEulerDegrees)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -587,7 +587,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, SetFromEulerRadians)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { @@ -600,7 +600,7 @@ namespace Benchmark BENCHMARK_F(BM_MathTransform, IsFinite)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& testData : m_testDataArray) { diff --git a/Code/Framework/AzCore/Tests/Math/Vector2PerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/Vector2PerformanceTests.cpp index 3ab306ff66..6c8e575481 100644 --- a/Code/Framework/AzCore/Tests/Math/Vector2PerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/Vector2PerformanceTests.cpp @@ -58,7 +58,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetSet)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZ::Vector2 v1, v2; float x = 0.0f, y = 0.0f; @@ -84,7 +84,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, ElementAccess)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZ::Vector2 v1, v2; float x = 0.0f, y = 0.0f; @@ -111,7 +111,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, CreateSelectCmpEqual)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -123,7 +123,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, CreateSelectCmpGreaterEqual)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -135,7 +135,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, CreateSelectCmpGreater)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -147,7 +147,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetNormalized)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -159,7 +159,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetNormalizedEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -171,7 +171,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, NormalizeWithLength)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -183,7 +183,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, NormalizeWithLengthEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -195,7 +195,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetNormalizedSafe)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -207,7 +207,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetNormalizedSafeEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -219,7 +219,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetDistance)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -231,7 +231,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetDistanceEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -243,7 +243,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, Lerp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -267,7 +267,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, Slerp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -291,7 +291,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, Nlerp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -315,7 +315,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, Dot)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -327,7 +327,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, Equality)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -339,7 +339,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, Inequality)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -351,7 +351,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, IsLessThan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -363,7 +363,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, IsLessEqualThan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -375,7 +375,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, IsGreaterThan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -387,7 +387,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, IsGreaterEqualThan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -399,7 +399,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetMin)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -411,7 +411,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetMax)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -423,7 +423,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetClamp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -435,7 +435,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, Sub)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -447,7 +447,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, Sum)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -459,7 +459,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, Mul)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -471,7 +471,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, Div)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -483,7 +483,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetSin)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -495,7 +495,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetCos)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -507,7 +507,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetSinCos)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -521,7 +521,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetAcos)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -533,7 +533,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetAtan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -545,7 +545,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetAtan2)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -557,7 +557,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetAngleMod)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -569,7 +569,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, Angle)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -581,7 +581,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, AngleDeg)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -593,7 +593,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetAbs)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -605,7 +605,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetReciprocal)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -617,7 +617,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetReciprocalEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -629,7 +629,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector2, GetProjected)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { diff --git a/Code/Framework/AzCore/Tests/Math/Vector3PerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/Vector3PerformanceTests.cpp index 5f33730bca..3e54e435d8 100644 --- a/Code/Framework/AzCore/Tests/Math/Vector3PerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/Vector3PerformanceTests.cpp @@ -58,7 +58,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetSet)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZ::Vector3 v1, v2, v3; float x = 0.0f, y = 0.0f, z = 0.0f; @@ -98,7 +98,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, ElementAccess)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZ::Vector3 v1, v2, v3; float x = 0.0f, y = 0.0f, z = 0.0f; @@ -138,7 +138,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, CreateSelectCmpEqual)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -150,7 +150,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, CreateSelectCmpGreaterEqual)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -162,7 +162,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, CreateSelectCmpGreater)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -174,7 +174,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetNormalized)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -186,7 +186,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetNormalizedEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -198,7 +198,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, NormalizeWithLength)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -210,7 +210,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, NormalizeWithLengthEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -222,7 +222,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetNormalizedSafe)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -234,7 +234,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetNormalizedSafeEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -246,7 +246,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetDistance)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -258,7 +258,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetDistanceEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -270,7 +270,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, Lerp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -294,7 +294,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, Slerp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -318,7 +318,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, Nlerp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -342,7 +342,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, Dot)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -354,7 +354,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, Cross)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -366,7 +366,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, Equality)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -378,7 +378,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, Inequality)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -390,7 +390,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, IsLessThan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -402,7 +402,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, IsLessEqualThan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -414,7 +414,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, IsGreaterThan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -426,7 +426,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, IsGreaterEqualThan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -438,7 +438,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetMin)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -450,7 +450,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetMax)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -462,7 +462,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetClamp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -474,7 +474,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, Sub)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -486,7 +486,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, Sum)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -498,7 +498,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, Mul)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -510,7 +510,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, Div)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -522,7 +522,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetSin)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -534,7 +534,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetCos)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -546,7 +546,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetSinCos)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -560,7 +560,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetAcos)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -572,7 +572,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetAtan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -584,7 +584,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetAngleMod)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -596,7 +596,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, Angle)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -608,7 +608,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, AngleDeg)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -620,7 +620,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetAbs)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -632,7 +632,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetReciprocal)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -644,7 +644,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetReciprocalEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -656,7 +656,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, IsPerpendicular)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -668,7 +668,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetOrthogonalVector)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -680,7 +680,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector3, GetProjected)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { diff --git a/Code/Framework/AzCore/Tests/Math/Vector4PerformanceTests.cpp b/Code/Framework/AzCore/Tests/Math/Vector4PerformanceTests.cpp index f12851b1ed..d2a5bbd46b 100644 --- a/Code/Framework/AzCore/Tests/Math/Vector4PerformanceTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/Vector4PerformanceTests.cpp @@ -60,7 +60,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetSet)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZ::Vector4 v1, v2, v3, v4; float x = 0.0f, y = 0.0f, z = 0.0f, w = 0.0f; @@ -117,7 +117,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, ElementAccess)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZ::Vector4 v1, v2, v3, v4; float x = 0.0f, y = 0.0f, z = 0.0f, w = 0.0f; @@ -174,7 +174,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, CreateSelectCmpEqual)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -186,7 +186,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, CreateSelectCmpGreaterEqual)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -198,7 +198,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, CreateSelectCmpGreater)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -210,7 +210,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetNormalized)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -222,7 +222,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetNormalizedEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -234,7 +234,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, NormalizeWithLength)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -246,7 +246,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, NormalizeWithLengthEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -258,7 +258,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetNormalizedSafe)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -270,7 +270,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetNormalizedSafeEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -282,7 +282,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetDistance)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -294,7 +294,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetDistanceEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -306,7 +306,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, Lerp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -330,7 +330,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, Slerp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -354,7 +354,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, Nlerp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -378,7 +378,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, Dot)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -390,7 +390,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, Dot3)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -402,7 +402,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetHomogenized)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -414,7 +414,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, Equality)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -426,7 +426,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, Inequality)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -438,7 +438,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, IsLessThan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -450,7 +450,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, IsLessEqualThan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -462,7 +462,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, IsGreaterThan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -474,7 +474,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, IsGreaterEqualThan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -486,7 +486,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetMin)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -498,7 +498,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetMax)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -510,7 +510,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetClamp)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -522,7 +522,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, Sub)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -534,7 +534,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, Sum)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -546,7 +546,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, Mul)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -558,7 +558,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, Div)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -570,7 +570,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetSin)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -582,7 +582,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetCos)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -594,7 +594,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetSinCos)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -608,7 +608,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetAcos)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -620,7 +620,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetAtan)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -632,7 +632,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetAngleMod)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -644,7 +644,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, Angle)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -656,7 +656,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, AngleDeg)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -668,7 +668,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetAbs)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -680,7 +680,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetReciprocal)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { @@ -692,7 +692,7 @@ namespace Benchmark BENCHMARK_F(BM_MathVector4, GetReciprocalEstimate)(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& vecData : m_vecDataArray) { diff --git a/Code/Framework/AzCore/Tests/Memory.cpp b/Code/Framework/AzCore/Tests/Memory.cpp index 0e0103d162..2be694c384 100644 --- a/Code/Framework/AzCore/Tests/Memory.cpp +++ b/Code/Framework/AzCore/Tests/Memory.cpp @@ -141,7 +141,6 @@ namespace UnitTest //////////////////////////////////////////////////////////////////////// // Create some threads and simulate concurrent allocation and deallocation - AZStd::chrono::system_clock::time_point startTime = AZStd::chrono::system_clock::now(); { AZStd::thread m_threads[m_maxNumThreads]; for (unsigned int i = 0; i < m_maxNumThreads; ++i) @@ -156,8 +155,6 @@ namespace UnitTest m_threads[i].join(); } } - //AZStd::chrono::microseconds exTime = AZStd::chrono::system_clock::now() - startTime; - //AZ_Printf("UnitTest::SystemAllocatorTest::deafult","Time: %d Ms\n",exTime.count()); ////////////////////////////////////////////////////////////////////////// AllocatorInstance::Destroy(); diff --git a/Code/Framework/AzCore/Tests/Memory/AllocatorBenchmarks.cpp b/Code/Framework/AzCore/Tests/Memory/AllocatorBenchmarks.cpp index e027b03a49..d778b90469 100644 --- a/Code/Framework/AzCore/Tests/Memory/AllocatorBenchmarks.cpp +++ b/Code/Framework/AzCore/Tests/Memory/AllocatorBenchmarks.cpp @@ -288,10 +288,10 @@ namespace Benchmark public: void Benchmark(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); - + AZStd::vector& perThreadAllocations = base::GetPerThreadAllocations(state.thread_index); const size_t numberOfAllocations = perThreadAllocations.size(); size_t totalAllocationSize = 0; @@ -300,7 +300,7 @@ namespace Benchmark const AllocationSizeArray& allocationArray = s_allocationSizes[TAllocationSize]; const size_t allocationSize = allocationArray[allocationIndex % allocationArray.size()]; totalAllocationSize += allocationSize; - + state.ResumeTiming(); perThreadAllocations[allocationIndex] = TestAllocatorType::Allocate(allocationSize, 0); state.PauseTiming(); @@ -319,6 +319,8 @@ namespace Benchmark TestAllocatorType::GarbageCollect(); state.SetItemsProcessed(numberOfAllocations); + + state.ResumeTiming(); } } }; @@ -333,7 +335,7 @@ namespace Benchmark public: void Benchmark(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); AZStd::vector& perThreadAllocations = base::GetPerThreadAllocations(state.thread_index); @@ -364,6 +366,8 @@ namespace Benchmark state.SetItemsProcessed(numberOfAllocations); TestAllocatorType::GarbageCollect(); + + state.ResumeTiming(); } } }; @@ -420,7 +424,7 @@ namespace Benchmark void Benchmark(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); @@ -519,6 +523,8 @@ namespace Benchmark state.SetItemsProcessed(itemsProcessed); TestAllocatorType::GarbageCollect(); + + state.ResumeTiming(); } } }; diff --git a/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp b/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp index 95b0626d7f..4efcd0d86e 100644 --- a/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp +++ b/Code/Framework/AzCore/Tests/Platform/Windows/Tests/IO/Streamer/StorageDriveTests_Windows.cpp @@ -981,7 +981,7 @@ namespace AZ::IO AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the // capture. Newer versions issue unused warning - auto callback = [numChunks, &numCallbacks, &waitForReads](FileRequestHandle request) + auto callback = [&numCallbacks, &waitForReads](FileRequestHandle request) AZ_POP_DISABLE_WARNING { IStreamer* streamer = Interface::Get(); @@ -1052,7 +1052,7 @@ namespace AZ::IO AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the // capture. Newer versions issue unused warning - auto callback = [numChunks, &waitForReads, &waitForSingleRead, &numReadCallbacks]([[maybe_unused]] FileRequestHandle request) + auto callback = [&waitForReads, &waitForSingleRead, &numReadCallbacks]([[maybe_unused]] FileRequestHandle request) AZ_POP_DISABLE_WARNING { numReadCallbacks++; @@ -1076,7 +1076,7 @@ namespace AZ::IO cancels.push_back(m_streamer->Cancel(requests[numChunks - i - 1])); AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the // capture. Newer versions issue unused warning - auto callback = [&numCancelCallbacks, &waitForCancels, numChunks](FileRequestHandle request) + auto callback = [&numCancelCallbacks, &waitForCancels](FileRequestHandle request) AZ_POP_DISABLE_WARNING { auto result = Interface::Get()->GetRequestStatus(request); @@ -1248,7 +1248,7 @@ namespace Benchmark AZStd::unique_ptr buffer(new char[FileSize]); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::binary_semaphore waitForReads; AZStd::atomic end; diff --git a/Code/Framework/AzCore/Tests/Settings/ConfigurableStackTests.cpp b/Code/Framework/AzCore/Tests/Settings/ConfigurableStackTests.cpp new file mode 100644 index 0000000000..0a69ac0053 --- /dev/null +++ b/Code/Framework/AzCore/Tests/Settings/ConfigurableStackTests.cpp @@ -0,0 +1,281 @@ +/* + * 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 +{ + struct ConfigInt + { + AZ_TYPE_INFO(UnitTest::ConfigInt, "{1FAF6E55-7FA4-4FFA-8C41-34F422B8E8AB}"); + AZ_CLASS_ALLOCATOR(ConfigInt, AZ::SystemAllocator, 0); + + int m_value; + + static void Reflect(AZ::ReflectContext* context) + { + if (auto sc = azrtti_cast(context)) + { + sc->Class()->Field("Value", &ConfigInt::m_value); + } + } + }; + + struct ConfigurableStackTests : public ScopedAllocatorSetupFixture + { + void Reflect(AZ::ReflectContext* context) + { + if (auto sc = azrtti_cast(context)) + { + AZ::JsonSystemComponent::Reflect(sc); + ConfigInt::Reflect(sc); + sc->RegisterGenericType>(); + } + else if (auto jrc = azrtti_cast(context)) + { + AZ::JsonSystemComponent::Reflect(jrc); + } + } + + void SetUp() override + { + ScopedAllocatorSetupFixture::SetUp(); + + m_serializeContext = AZStd::make_unique(); + m_jsonRegistrationContext = AZStd::make_unique(); + + Reflect(m_serializeContext.get()); + Reflect(m_jsonRegistrationContext.get()); + + m_deserializationSettings.m_registrationContext = m_jsonRegistrationContext.get(); + m_deserializationSettings.m_serializeContext = m_serializeContext.get(); + } + + void TearDown() + { + m_jsonRegistrationContext->EnableRemoveReflection(); + Reflect(m_jsonRegistrationContext.get()); + m_jsonRegistrationContext->DisableRemoveReflection(); + + m_serializeContext->EnableRemoveReflection(); + Reflect(m_serializeContext.get()); + m_serializeContext->DisableRemoveReflection(); + + ScopedAllocatorSetupFixture::TearDown(); + } + + void ObjectTest(AZStd::string_view jsonText) + { + AZ::ConfigurableStack stack; + + rapidjson::Document document; + document.Parse(jsonText.data(), jsonText.length()); + ASSERT_FALSE(document.HasParseError()); + AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(stack, document, m_deserializationSettings); + ASSERT_EQ(AZ::JsonSerializationResult::Processing::Completed, result.GetProcessing()); + ASSERT_EQ(4, stack.size()); + + int numberCounter = 0; + int valueCounter = 42; + for (auto& [name, value] : stack) + { + EXPECT_STREQ(AZStd::string::format("Value%i", numberCounter).c_str(), name.c_str()); + EXPECT_EQ(valueCounter, value->m_value); + numberCounter++; + valueCounter++; + } + } + + AZStd::unique_ptr m_serializeContext; + AZStd::unique_ptr m_jsonRegistrationContext; + AZ::JsonDeserializerSettings m_deserializationSettings; + }; + + TEST_F(ConfigurableStackTests, DeserializeArray) + { + AZ::ConfigurableStack stack; + + rapidjson::Document document; + document.Parse( + R"([ + { "Value": 42 }, + { "Value": 43 }, + { "Value": 44 }, + { "Value": 45 } + ])"); + ASSERT_FALSE(document.HasParseError()); + AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(stack, document, m_deserializationSettings); + ASSERT_EQ(AZ::JsonSerializationResult::Processing::Completed, result.GetProcessing()); + ASSERT_EQ(4, stack.size()); + + int numberCounter = 0; + int valueCounter = 42; + for (auto& [name, value] : stack) + { + EXPECT_STREQ(AZStd::to_string(numberCounter).c_str(), name.c_str()); + EXPECT_EQ(valueCounter, value->m_value); + numberCounter++; + valueCounter++; + } + } + + TEST_F(ConfigurableStackTests, DeserializeObject) + { + ObjectTest( + R"({ + "Value0": { "Value": 42 }, + "Value1": { "Value": 43 }, + "Value2": { "Value": 44 }, + "Value3": { "Value": 45 } + })"); + } + + TEST_F(ConfigurableStackTests, DeserializeObjectWithLateBefore) + { + ObjectTest( + R"({ + "Value0": { "Value": 42 }, + "Value2": { "Value": 44 }, + "Value3": { "Value": 45 }, + "Value1": + { + "$stack_before": "Value2", + "Value": 43 + } + })"); + } + + TEST_F(ConfigurableStackTests, DeserializeObjectWithEarlyBefore) + { + ObjectTest( + R"({ + "Value1": + { + "$stack_before": "Value2", + "Value": 43 + }, + "Value0": { "Value": 42 }, + "Value2": { "Value": 44 }, + "Value3": { "Value": 45 } + })"); + } + + TEST_F(ConfigurableStackTests, DeserializeObjectWithLateAfter) + { + ObjectTest( + R"({ + "Value0": { "Value": 42 }, + "Value2": { "Value": 44 }, + "Value3": { "Value": 45 }, + "Value1": + { + "$stack_after": "Value0", + "Value": 43 + } + })"); + } + + TEST_F(ConfigurableStackTests, DeserializeObjectWithEarlyAfter) + { + ObjectTest( + R"({ + "Value1": + { + "$stack_after": "Value0", + "Value": 43 + }, + "Value0": { "Value": 42 }, + "Value2": { "Value": 44 }, + "Value3": { "Value": 45 } + })"); + } + + TEST_F(ConfigurableStackTests, DeserializeObjectWithBeforeFirst) + { + ObjectTest( + R"({ + "Value1": { "Value": 43 }, + "Value2": { "Value": 44 }, + "Value3": { "Value": 45 }, + "Value0": + { + "$stack_before": "Value1", + "Value": 42 + } + + })"); + } + + TEST_F(ConfigurableStackTests, DeserializeObjectWithInsertAfterLast) + { + ObjectTest( + R"({ + "Value3": + { + "$stack_after": "Value2", + "Value": 45 + }, + "Value0": { "Value": 42 }, + "Value1": { "Value": 43 }, + "Value2": { "Value": 44 } + })"); + } + + TEST_F(ConfigurableStackTests, DeserializeObjectWithInvalidTarget) + { + AZ::ConfigurableStack stack; + + rapidjson::Document document; + document.Parse( + R"({ + "Value1": + { + "$stack_after": "airplane", + "Value": 43 + }, + "Value0": { "Value": 42 }, + "Value2": { "Value": 44 }, + "Value3": { "Value": 45 } + })"); + ASSERT_FALSE(document.HasParseError()); + AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(stack, document, m_deserializationSettings); + EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, result.GetProcessing()); + EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::PartialSkip, result.GetOutcome()); + EXPECT_EQ(3, stack.size()); + } + + TEST_F(ConfigurableStackTests, DeserializeObjectWithInvalidTargetType) + { + AZ::ConfigurableStack stack; + + rapidjson::Document document; + document.Parse( + R"({ + "Value1": + { + "$stack_after": 42, + "Value": 43 + }, + "Value0": { "Value": 42 }, + "Value2": { "Value": 44 }, + "Value3": { "Value": 45 } + })"); + ASSERT_FALSE(document.HasParseError()); + AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(stack, document, m_deserializationSettings); + EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, result.GetProcessing()); + EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::PartialSkip, result.GetOutcome()); + EXPECT_EQ(3, stack.size()); + } +} // namespace UnitTest diff --git a/Code/Framework/AzCore/Tests/TaskTests.cpp b/Code/Framework/AzCore/Tests/TaskTests.cpp index 53fa89900e..7d805ce1f7 100644 --- a/Code/Framework/AzCore/Tests/TaskTests.cpp +++ b/Code/Framework/AzCore/Tests/TaskTests.cpp @@ -677,7 +677,7 @@ namespace Benchmark [] { }); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { TaskGraphEvent ev; graph->SubmitOnExecutor(*executor, &ev); @@ -699,7 +699,7 @@ namespace Benchmark }); a.Precedes(b); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { TaskGraphEvent ev; graph->SubmitOnExecutor(*executor, &ev); @@ -729,7 +729,7 @@ namespace Benchmark e.Follows(a, b, c, d); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { TaskGraphEvent ev; graph->SubmitOnExecutor(*executor, &ev); diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index 23ee2e3c57..26181db90b 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -76,6 +76,7 @@ set(FILES Name/NameTests.cpp RTTI/TypeSafeIntegralTests.cpp Settings/CommandLineTests.cpp + Settings/ConfigurableStackTests.cpp Settings/SettingsRegistryTests.cpp Settings/SettingsRegistryConsoleUtilsTests.cpp Settings/SettingsRegistryMergeUtilsTests.cpp @@ -230,6 +231,8 @@ set(FILES DOM/DomPatchBenchmarks.cpp DOM/DomValueTests.cpp DOM/DomValueBenchmarks.cpp + DOM/DomPrefixTreeTests.cpp + DOM/DomPrefixTreeBenchmarks.cpp ) # Prevent the following files from being grouped in UNITY builds diff --git a/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h b/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h index 7909a7ec83..bff29334d1 100644 --- a/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h +++ b/Code/Framework/AzFramework/AzFramework/Terrain/TerrainDataRequestBus.h @@ -8,10 +8,12 @@ #pragma once #include +#include #include #include #include #include +#include #include #include #include @@ -138,28 +140,28 @@ namespace AzFramework //! Given a list of XY coordinates, call the provided callback function with surface data corresponding to each //! XY coordinate in the list. - virtual void ProcessHeightsFromList(const AZStd::span& inPositions, + virtual void ProcessHeightsFromList(const AZStd::span& inPositions, SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const = 0; - virtual void ProcessNormalsFromList(const AZStd::span& inPositions, + virtual void ProcessNormalsFromList(const AZStd::span& inPositions, SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const = 0; - virtual void ProcessSurfaceWeightsFromList(const AZStd::span& inPositions, + virtual void ProcessSurfaceWeightsFromList(const AZStd::span& inPositions, SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const = 0; - virtual void ProcessSurfacePointsFromList(const AZStd::span& inPositions, + virtual void ProcessSurfacePointsFromList(const AZStd::span& inPositions, SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const = 0; - virtual void ProcessHeightsFromListOfVector2(const AZStd::span& inPositions, + virtual void ProcessHeightsFromListOfVector2(const AZStd::span& inPositions, SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const = 0; - virtual void ProcessNormalsFromListOfVector2(const AZStd::span& inPositions, + virtual void ProcessNormalsFromListOfVector2(const AZStd::span& inPositions, SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const = 0; - virtual void ProcessSurfaceWeightsFromListOfVector2(const AZStd::span& inPositions, + virtual void ProcessSurfaceWeightsFromListOfVector2(const AZStd::span& inPositions, SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const = 0; - virtual void ProcessSurfacePointsFromListOfVector2(const AZStd::span& inPositions, + virtual void ProcessSurfacePointsFromListOfVector2(const AZStd::span& inPositions, SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const = 0; @@ -193,6 +195,147 @@ namespace AzFramework //! Given a ray, return the closest intersection with terrain. virtual RenderGeometry::RayResult GetClosestIntersection(const RenderGeometry::RayRequest& ray) const = 0; + //! A JobContext used to run jobs spawned by calls to the various Process*Async functions. + class TerrainJobContext : public AZ::JobContext + { + public: + TerrainJobContext(AZ::JobManager& jobManager, + int numJobsToComplete) + : JobContext(jobManager) + , m_numJobsToComplete(numJobsToComplete) + { + } + + // When a terrain job context is cancelled, all associated + // jobs are still guaranteed to at least begin processing, + // and if any ProcessAsyncParams::m_completionCallback was + // set it's guaranteed to be called even in the event of a + // cancellation. If a job only begins processing after its + // associated job context has been cancelled, no processing + // will occur and the callback will be invoked immediately, + // otherwise the job may either run to completion or cease + // processing early; the callback is invoked in all cases, + // provided one was specified with the original request. + void Cancel() { m_isCancelled = true; } + + // Was this TerrainJobContext cancelled? + bool IsCancelled() const { return m_isCancelled; } + + // Called by the TerrainSystem when a job associated with + // this TerrainJobContext completes. Returns true if this + // was the final job to be completed, or false otherwise. + bool OnJobCompleted() { return (--m_numJobsToComplete == 0); } + + private: + AZStd::atomic_int m_numJobsToComplete = 0; + AZStd::atomic_bool m_isCancelled = false; + }; + + //! Alias for an optional callback function to invoke when the various Process*Async functions complete. + //! The TerrainJobContext, returned from the original Process*Async function call, is passed as a param + //! to the callback function so it can be queried to see if the job was cancelled or completed normally. + typedef AZStd::function)> ProcessAsyncCompleteCallback; + + //! A parameter group struct that can optionally be passed to the various Process*Async API functions. + struct ProcessAsyncParams + { + //! The default minimum number ofpositions per async terrain request job. + static constexpr int32_t MinPositionsPerJobDefault = 8; + + //! The default number of jobs which async terrain requests will be split into. + static constexpr int32_t NumJobsDefault = 1; + + //! The maximum number of jobs which async terrain requests will be split into. + //! This is not the value itself, rather a constant that can be used to request + //! the work be split into the maximum number of job manager threads available. + static constexpr int32_t NumJobsMax = -1; + + //! The desired number of jobs to split async terrain requests into. + //! The actual value used will be clamped to the number of available job manager threads. + //! + //! Note: Currently, splitting the work over multiple threads causes contention when + //! locking various mutexes, resulting in slower overall wall time for async + //! requests split over multiple threads vs one where all the work is done on + //! a single thread. The latter is still preferable over a regular synchronous + //! call because it is just as quick and prevents the main thread from blocking. + //! This note should be removed once the mutex contention issues have been addressed. + int32_t m_desiredNumberOfJobs = NumJobsDefault; + + //! The minimum number of positions per async terrain request job. + int32_t m_minPositionsPerJob = MinPositionsPerJobDefault; + + //! The callback function that will be invoked when a call to a Process*Async function completes. + //! If the job is cancelled, the completion callback will not be invoked. + ProcessAsyncCompleteCallback m_completionCallback = nullptr; + }; + + //! Asynchronous versions of the various 'Process*' API functions declared above. + //! It's the responsibility of the caller to ensure all callbacks are threadsafe. + virtual AZStd::shared_ptr ProcessHeightsFromListAsync( + const AZStd::span& inPositions, + SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const = 0; + virtual AZStd::shared_ptr ProcessNormalsFromListAsync( + const AZStd::span& inPositions, + SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const = 0; + virtual AZStd::shared_ptr ProcessSurfaceWeightsFromListAsync( + const AZStd::span& inPositions, + SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const = 0; + virtual AZStd::shared_ptr ProcessSurfacePointsFromListAsync( + const AZStd::span& inPositions, + SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const = 0; + virtual AZStd::shared_ptr ProcessHeightsFromListOfVector2Async( + const AZStd::span& inPositions, + SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const = 0; + virtual AZStd::shared_ptr ProcessNormalsFromListOfVector2Async( + const AZStd::span& inPositions, + SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const = 0; + virtual AZStd::shared_ptr ProcessSurfaceWeightsFromListOfVector2Async( + const AZStd::span& inPositions, + SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const = 0; + virtual AZStd::shared_ptr ProcessSurfacePointsFromListOfVector2Async( + const AZStd::span& inPositions, + SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const = 0; + virtual AZStd::shared_ptr ProcessHeightsFromRegionAsync( + const AZ::Aabb& inRegion, + const AZ::Vector2& stepSize, + SurfacePointRegionFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const = 0; + virtual AZStd::shared_ptr ProcessNormalsFromRegionAsync( + const AZ::Aabb& inRegion, + const AZ::Vector2& stepSize, + SurfacePointRegionFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const = 0; + virtual AZStd::shared_ptr ProcessSurfaceWeightsFromRegionAsync( + const AZ::Aabb& inRegion, + const AZ::Vector2& stepSize, + SurfacePointRegionFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const = 0; + virtual AZStd::shared_ptr ProcessSurfacePointsFromRegionAsync( + const AZ::Aabb& inRegion, + const AZ::Vector2& stepSize, + SurfacePointRegionFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const = 0; + private: // Private variations of the GetSurfacePoint API exposed to BehaviorContext that returns a value instead of // using an "out" parameter. The "out" parameter is useful for reusing memory allocated in SurfacePoint when diff --git a/Code/Framework/AzFramework/Platform/Windows/AzFramework/Process/ProcessWatcher_Win.cpp b/Code/Framework/AzFramework/Platform/Windows/AzFramework/Process/ProcessWatcher_Win.cpp index b0ef2f09a4..387a1abfe7 100644 --- a/Code/Framework/AzFramework/Platform/Windows/AzFramework/Process/ProcessWatcher_Win.cpp +++ b/Code/Framework/AzFramework/Platform/Windows/AzFramework/Process/ProcessWatcher_Win.cpp @@ -374,8 +374,6 @@ namespace AzFramework // is double-quoted, then the double-quotes must be escaped properly otherwise // it will be absorbed by the native argument parser and possibly evaluated as // multiple values for arguments - AZStd::string_view escapedDoubleQuote = R"("\")"; - AZStd::vector preprocessedCommandArray; for (const auto& commandArg : commandLineArray) diff --git a/Code/Framework/AzFramework/Tests/Mocks/Terrain/MockTerrainDataRequestBus.h b/Code/Framework/AzFramework/Tests/Mocks/Terrain/MockTerrainDataRequestBus.h index 4b9658eebb..d038d5dd24 100644 --- a/Code/Framework/AzFramework/Tests/Mocks/Terrain/MockTerrainDataRequestBus.h +++ b/Code/Framework/AzFramework/Tests/Mocks/Terrain/MockTerrainDataRequestBus.h @@ -77,21 +77,21 @@ namespace UnitTest MOCK_CONST_METHOD5( GetSurfacePointFromFloats, void(float, float, AzFramework::SurfaceData::SurfacePoint&, Sampler, bool*)); MOCK_CONST_METHOD3( - ProcessHeightsFromList, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); + ProcessHeightsFromList, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); MOCK_CONST_METHOD3( - ProcessNormalsFromList, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); + ProcessNormalsFromList, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); MOCK_CONST_METHOD3( - ProcessSurfaceWeightsFromList, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); + ProcessSurfaceWeightsFromList, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); MOCK_CONST_METHOD3( - ProcessSurfacePointsFromList, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); + ProcessSurfacePointsFromList, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); MOCK_CONST_METHOD3( - ProcessHeightsFromListOfVector2, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); + ProcessHeightsFromListOfVector2, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); MOCK_CONST_METHOD3( - ProcessNormalsFromListOfVector2, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); + ProcessNormalsFromListOfVector2, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); MOCK_CONST_METHOD3( - ProcessSurfaceWeightsFromListOfVector2, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); + ProcessSurfaceWeightsFromListOfVector2, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); MOCK_CONST_METHOD3( - ProcessSurfacePointsFromListOfVector2, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); + ProcessSurfacePointsFromListOfVector2, void(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); MOCK_CONST_METHOD2( GetNumSamplesFromRegion, AZStd::pair(const AZ::Aabb&, const AZ::Vector2&)); MOCK_CONST_METHOD4( @@ -106,5 +106,30 @@ namespace UnitTest GetTerrainRaycastEntityContextId, AzFramework::EntityContextId()); MOCK_CONST_METHOD1( GetClosestIntersection, AzFramework::RenderGeometry::RayResult(const AzFramework::RenderGeometry::RayRequest&)); + MOCK_CONST_METHOD4( + ProcessHeightsFromListAsync, AZStd::shared_ptr(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr)); + MOCK_CONST_METHOD4( + ProcessNormalsFromListAsync, AZStd::shared_ptr(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr)); + MOCK_CONST_METHOD4( + ProcessSurfaceWeightsFromListAsync, AZStd::shared_ptr(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr)); + MOCK_CONST_METHOD4( + ProcessSurfacePointsFromListAsync, AZStd::shared_ptr(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr)); + MOCK_CONST_METHOD4( + ProcessHeightsFromListOfVector2Async, AZStd::shared_ptr(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr)); + MOCK_CONST_METHOD4( + ProcessNormalsFromListOfVector2Async, AZStd::shared_ptr(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr)); + MOCK_CONST_METHOD4( + ProcessSurfaceWeightsFromListOfVector2Async, AZStd::shared_ptr(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr)); + MOCK_CONST_METHOD4( + ProcessSurfacePointsFromListOfVector2Async, AZStd::shared_ptr(const AZStd::span&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr)); + MOCK_CONST_METHOD5( + ProcessHeightsFromRegionAsync, AZStd::shared_ptr(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler, AZStd::shared_ptr)); + MOCK_CONST_METHOD5( + ProcessNormalsFromRegionAsync, AZStd::shared_ptr(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler, AZStd::shared_ptr)); + MOCK_CONST_METHOD5( + ProcessSurfaceWeightsFromRegionAsync, AZStd::shared_ptr(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler, AZStd::shared_ptr)); + MOCK_CONST_METHOD5( + ProcessSurfacePointsFromRegionAsync, AZStd::shared_ptr(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler, AZStd::shared_ptr)); + }; } // namespace UnitTest diff --git a/Code/Framework/AzFramework/Tests/OctreePerformanceTests.cpp b/Code/Framework/AzFramework/Tests/OctreePerformanceTests.cpp index 64fba61e2c..7ab2638af6 100644 --- a/Code/Framework/AzFramework/Tests/OctreePerformanceTests.cpp +++ b/Code/Framework/AzFramework/Tests/OctreePerformanceTests.cpp @@ -142,7 +142,7 @@ namespace Benchmark BENCHMARK_F(BM_Octree, InsertDelete1000)(benchmark::State& state) { constexpr uint32_t EntryCount = 1000; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { InsertEntries(EntryCount); RemoveEntries(EntryCount); @@ -152,7 +152,7 @@ namespace Benchmark BENCHMARK_F(BM_Octree, InsertDelete10000)(benchmark::State& state) { constexpr uint32_t EntryCount = 10000; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { InsertEntries(EntryCount); RemoveEntries(EntryCount); @@ -162,7 +162,7 @@ namespace Benchmark BENCHMARK_F(BM_Octree, InsertDelete100000)(benchmark::State& state) { constexpr uint32_t EntryCount = 100000; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { InsertEntries(EntryCount); RemoveEntries(EntryCount); @@ -172,7 +172,7 @@ namespace Benchmark BENCHMARK_F(BM_Octree, InsertDelete1000000)(benchmark::State& state) { constexpr uint32_t EntryCount = 1000000; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { InsertEntries(EntryCount); RemoveEntries(EntryCount); @@ -183,7 +183,7 @@ namespace Benchmark { constexpr uint32_t EntryCount = 1000; InsertEntries(EntryCount); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& queryData : m_queryDataArray) { @@ -197,7 +197,7 @@ namespace Benchmark { constexpr uint32_t EntryCount = 10000; InsertEntries(EntryCount); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& queryData : m_queryDataArray) { @@ -211,7 +211,7 @@ namespace Benchmark { constexpr uint32_t EntryCount = 100000; InsertEntries(EntryCount); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& queryData : m_queryDataArray) { @@ -225,7 +225,7 @@ namespace Benchmark { constexpr uint32_t EntryCount = 1000000; InsertEntries(EntryCount); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& queryData : m_queryDataArray) { @@ -239,7 +239,7 @@ namespace Benchmark { constexpr uint32_t EntryCount = 1000; InsertEntries(EntryCount); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& queryData : m_queryDataArray) { @@ -253,7 +253,7 @@ namespace Benchmark { constexpr uint32_t EntryCount = 10000; InsertEntries(EntryCount); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& queryData : m_queryDataArray) { @@ -267,7 +267,7 @@ namespace Benchmark { constexpr uint32_t EntryCount = 100000; InsertEntries(EntryCount); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& queryData : m_queryDataArray) { @@ -281,7 +281,7 @@ namespace Benchmark { constexpr uint32_t EntryCount = 1000000; InsertEntries(EntryCount); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& queryData : m_queryDataArray) { @@ -295,7 +295,7 @@ namespace Benchmark { constexpr uint32_t EntryCount = 1000; InsertEntries(EntryCount); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& queryData : m_queryDataArray) { @@ -309,7 +309,7 @@ namespace Benchmark { constexpr uint32_t EntryCount = 10000; InsertEntries(EntryCount); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& queryData : m_queryDataArray) { @@ -323,7 +323,7 @@ namespace Benchmark { constexpr uint32_t EntryCount = 100000; InsertEntries(EntryCount); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& queryData : m_queryDataArray) { @@ -337,7 +337,7 @@ namespace Benchmark { constexpr uint32_t EntryCount = 1000000; InsertEntries(EntryCount); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (auto& queryData : m_queryDataArray) { diff --git a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ActionDispatcher.h b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ActionDispatcher.h index 99f11c37da..f056493ffd 100644 --- a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ActionDispatcher.h +++ b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ActionDispatcher.h @@ -46,9 +46,9 @@ namespace AzManipulatorTestFramework //! Send a double click event. DerivedDispatcherT* MouseLButtonDoubleClick(); //! Set the keyboard modifier button down. - DerivedDispatcherT* KeyboardModifierDown(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier); + DerivedDispatcherT* KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier); //! Set the keyboard modifier button up. - DerivedDispatcherT* KeyboardModifierUp(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier); + DerivedDispatcherT* KeyboardModifierUp(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier); //! Set the mouse position to the specified screen space position. DerivedDispatcherT* MousePosition(const AzFramework::ScreenPoint& position); //! Expect the selected manipulator to be interacting. @@ -81,8 +81,8 @@ namespace AzManipulatorTestFramework virtual void MouseMButtonUpImpl() = 0; virtual void MouseLButtonDoubleClickImpl() = 0; virtual void MousePositionImpl(const AzFramework::ScreenPoint& position) = 0; - virtual void KeyboardModifierDownImpl(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier) = 0; - virtual void KeyboardModifierUpImpl(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier) = 0; + virtual void KeyboardModifierDownImpl(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier) = 0; + virtual void KeyboardModifierUpImpl(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier) = 0; virtual void ExpectManipulatorBeingInteractedImpl() = 0; virtual void ExpectManipulatorNotBeingInteractedImpl() = 0; virtual void SetEntityWorldTransformImpl(AZ::EntityId entityId, const AZ::Transform& transform) = 0; @@ -94,7 +94,7 @@ namespace AzManipulatorTestFramework bool m_logging = false; private: - const char* KeyboardModifierString(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier); + const char* KeyboardModifierString(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier); static void DebugPrint(const char* format, ...); }; @@ -215,7 +215,7 @@ namespace AzManipulatorTestFramework template const char* ActionDispatcher::KeyboardModifierString( - const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier) + const AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier) { using namespace AzToolsFramework::ViewportInteraction; switch (keyModifier) @@ -235,7 +235,7 @@ namespace AzManipulatorTestFramework template DerivedDispatcherT* ActionDispatcher::KeyboardModifierDown( - const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier) + const AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier) { Log("Keyboard modifier down: %s", KeyboardModifierString(keyModifier)); KeyboardModifierDownImpl(keyModifier); @@ -244,7 +244,7 @@ namespace AzManipulatorTestFramework template DerivedDispatcherT* ActionDispatcher::KeyboardModifierUp( - const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier) + const AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier) { Log("Keyboard modifier up: %s", KeyboardModifierString(keyModifier)); KeyboardModifierUpImpl(keyModifier); diff --git a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ImmediateModeActionDispatcher.h b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ImmediateModeActionDispatcher.h index 8a05a15fd9..16b8966ba4 100644 --- a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ImmediateModeActionDispatcher.h +++ b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ImmediateModeActionDispatcher.h @@ -66,8 +66,8 @@ namespace AzManipulatorTestFramework void MouseMButtonUpImpl() override; void MouseLButtonDoubleClickImpl() override; void MousePositionImpl(const AzFramework::ScreenPoint& position) override; - void KeyboardModifierDownImpl(const KeyboardModifier& keyModifier) override; - void KeyboardModifierUpImpl(const KeyboardModifier& keyModifier) override; + void KeyboardModifierDownImpl(KeyboardModifier keyModifier) override; + void KeyboardModifierUpImpl(KeyboardModifier keyModifier) override; void ExpectManipulatorBeingInteractedImpl() override; void ExpectManipulatorNotBeingInteractedImpl() override; void SetEntityWorldTransformImpl(AZ::EntityId entityId, const AZ::Transform& transform) override; diff --git a/Code/Framework/AzManipulatorTestFramework/Source/ImmediateModeActionDispatcher.cpp b/Code/Framework/AzManipulatorTestFramework/Source/ImmediateModeActionDispatcher.cpp index 715c2b1083..eb578162a6 100644 --- a/Code/Framework/AzManipulatorTestFramework/Source/ImmediateModeActionDispatcher.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Source/ImmediateModeActionDispatcher.cpp @@ -126,12 +126,12 @@ namespace AzManipulatorTestFramework m_manipulatorViewportInteraction.GetManipulatorManager().ConsumeMouseInteractionEvent(*m_event); } - void ImmediateModeActionDispatcher::KeyboardModifierDownImpl(const KeyboardModifier& keyModifier) + void ImmediateModeActionDispatcher::KeyboardModifierDownImpl(const KeyboardModifier keyModifier) { ToggleOn(GetMouseInteractionEvent()->m_mouseInteraction.m_keyboardModifiers.m_keyModifiers, keyModifier); } - void ImmediateModeActionDispatcher::KeyboardModifierUpImpl(const KeyboardModifier& keyModifier) + void ImmediateModeActionDispatcher::KeyboardModifierUpImpl(const KeyboardModifier keyModifier) { ToggleOff(GetMouseInteractionEvent()->m_mouseInteraction.m_keyboardModifiers.m_keyModifiers, keyModifier); } diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp index 7e78f64778..26b9e13e18 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp @@ -276,7 +276,6 @@ namespace AzQtComponents */ void FancyDocking::updateDockingGeometry() { - QRect totalScreenRect; int numScreens = QApplication::screens().count(); #ifdef AZ_PLATFORM_WINDOWS @@ -286,6 +285,9 @@ namespace AzQtComponents m_perScreenFullScreenWidgets.clear(); #endif +#if !defined(AZ_PLATFORM_WINDOWS) + QRect totalScreenRect; +#endif for (int i = 0; i < numScreens; ++i) { #ifdef AZ_PLATFORM_WINDOWS diff --git a/Code/Framework/AzTest/AzTest/AzTest.h b/Code/Framework/AzTest/AzTest/AzTest.h index 4b038cabd4..d40eb0eb75 100644 --- a/Code/Framework/AzTest/AzTest/AzTest.h +++ b/Code/Framework/AzTest/AzTest/AzTest.h @@ -164,6 +164,16 @@ namespace AZ m_envs.push_back(std::move(env)); } + // Remove a registered benchmark from the registry + void RemoveBenchmarkEnvironment(BenchmarkEnvironmentBase* env) + { + auto RemoveBenchmarkFunc = [env](const std::unique_ptr& envElement) + { + return envElement.get() == env; + }; + m_envs.erase(std::remove_if(m_envs.begin(), m_envs.end(), std::move(RemoveBenchmarkFunc))); + } + std::vector>& GetBenchmarkEnvironments() { return m_envs; @@ -194,21 +204,26 @@ namespace AZ return *benchmarkEnv; } - template - std::array RegisterBenchmarkEnvironments() + /* + * An RAII wrapper about registering a BenchmarkEnvironment with the BenchmarkRegistry + * It will unregister the BenchmarkEnvironment with the BenchmarkRegistry on destruction + */ + struct ScopedRegisterBenchmarkEnvironment { - constexpr size_t EnvironmentCount{ sizeof...(Ts) }; - if constexpr (EnvironmentCount) - { - std::array benchmarkEnvs{ { &RegisterBenchmarkEnvironment()... } }; - return benchmarkEnvs; - } - else + template + ScopedRegisterBenchmarkEnvironment(T& benchmarkEnv) + : m_benchmarkEnv(benchmarkEnv) + {} + ~ScopedRegisterBenchmarkEnvironment() { - std::array benchmarkEnvs{}; - return benchmarkEnvs; + if (auto benchmarkRegistry = AZ::Environment::FindVariable(s_benchmarkEnvironmentName); + benchmarkRegistry != nullptr) + { + benchmarkRegistry->RemoveBenchmarkEnvironment(&m_benchmarkEnv); + } } - } + BenchmarkEnvironmentBase& m_benchmarkEnv; + }; #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //! listener class to capture and print test output for embedded platforms @@ -276,7 +291,7 @@ namespace AZ #define AZ_BENCHMARK_HOOK_ENV(TEST_ENV) \ AZTEST_EXPORT int AzRunBenchmarks(int argc, char** argv) \ { \ - AZ::Test::RegisterBenchmarkEnvironments(); \ + AZ::Test::ScopedRegisterBenchmarkEnvironment scopedBenchmarkEnv(AZ::Test::RegisterBenchmarkEnvironment()); \ auto benchmarkEnvRegistry = AZ::Environment::FindVariable(AZ::Test::s_benchmarkEnvironmentName); \ std::vector>* benchmarkEnvs = benchmarkEnvRegistry ? &(benchmarkEnvRegistry->GetBenchmarkEnvironments()) : nullptr; \ if (benchmarkEnvs != nullptr) \ @@ -307,7 +322,6 @@ AZTEST_EXPORT int AzRunBenchmarks(int argc, char** argv) \ #define AZ_BENCHMARK_HOOK() \ AZTEST_EXPORT int AzRunBenchmarks(int argc, char** argv) \ { \ - AZ::Test::RegisterBenchmarkEnvironments<>(); \ auto benchmarkEnvRegistry = AZ::Environment::FindVariable(AZ::Test::s_benchmarkEnvironmentName); \ std::vector>* benchmarkEnvs = benchmarkEnvRegistry ? &(benchmarkEnvRegistry->GetBenchmarkEnvironments()) : nullptr; \ if (benchmarkEnvs != nullptr) \ diff --git a/Code/Framework/AzTest/AzTest/GemTestEnvironment.cpp b/Code/Framework/AzTest/AzTest/GemTestEnvironment.cpp index e862cdfa94..47eb71898a 100644 --- a/Code/Framework/AzTest/AzTest/GemTestEnvironment.cpp +++ b/Code/Framework/AzTest/AzTest/GemTestEnvironment.cpp @@ -52,7 +52,6 @@ namespace AZ GemTestEnvironment::GemTestEnvironment() { - m_parameters = new Parameters; } void GemTestEnvironment::AddDynamicModulePaths(const AZStd::vector& dynamicModulePaths) @@ -84,6 +83,8 @@ namespace AZ AZ::AllocatorInstance::Create(); + m_parameters = new Parameters; + AddGemsAndComponents(); PreCreateApplication(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorEntityAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorEntityAPI.h index 3b5e5c9dc3..9ab732938c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorEntityAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorEntityAPI.h @@ -8,14 +8,13 @@ #pragma once -#include #include #include +#include + namespace AzToolsFramework { - using EntityIdList = AZStd::vector; - /*! * EditorEntityAPI * Handles basic Entity operations diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EntityCompositionNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EntityCompositionNotificationBus.h index f44ccb2806..420f66e54d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EntityCompositionNotificationBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EntityCompositionNotificationBus.h @@ -8,20 +8,19 @@ #pragma once #include -#include -#include #include #include +#include +#include + +#include namespace AzToolsFramework { - using EntityIdList = AZStd::vector; - class EntityCompositionNotifications : public AZ::EBusTraits { public: - /*! * Notification that the specified entities are about to have their composition changed due to user interaction in the editor * diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h index bca68a6c0a..f84ff5f312 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h @@ -8,40 +8,40 @@ #pragma once #include +#include +#include #include #include #include #include -#include -#include -#include +#include #include #include -#include -#include -#include #include +#include +#include +#include + namespace AZ { class Entity; class Vector2; - class Entity; -} +} // namespace AZ -class QMenu; -class QWidget; +struct IEditor; class QApplication; class QDockWidget; class QMainWindow; -struct IEditor; +class QMenu; +class QWidget; namespace AzToolsFramework { struct ViewPaneOptions; - class PreemptiveUndoCache; class EntityPropertyEditor; + class PreemptiveUndoCache; namespace UndoSystem { @@ -54,10 +54,7 @@ namespace AzToolsFramework class AssetSelectionModel; } - using EntityIdList = AZStd::vector; - using EntityList = AZStd::vector; using ClassDataList = AZStd::vector; - using EntityIdSet = AZStd::unordered_set; //! Return true to accept this type of component. using ComponentFilter = AZStd::function; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserComponent.cpp index 172bd3a29c..a91102b85c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserComponent.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -71,9 +70,9 @@ namespace AzToolsFramework AssetBrowserInteractionNotificationBus::Handler::BusConnect(); using namespace Thumbnailer; - ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(FolderThumbnailCache), ThumbnailContext::DefaultContext); - ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(SourceThumbnailCache), ThumbnailContext::DefaultContext); - ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(ProductThumbnailCache), ThumbnailContext::DefaultContext); + ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(FolderThumbnailCache)); + ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(SourceThumbnailCache)); + ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(ProductThumbnailCache)); AzFramework::SocketConnection* socketConn = AzFramework::SocketConnection::GetInstance(); AZ_Assert(socketConn, "AzToolsFramework::AssetBrowser::AssetBrowserComponent requires a valid socket conection!"); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Thumbnails/ProductThumbnail.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Thumbnails/ProductThumbnail.cpp index 5dd0cf5170..bc7ad75957 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Thumbnails/ProductThumbnail.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Thumbnails/ProductThumbnail.cpp @@ -75,7 +75,9 @@ namespace AzToolsFramework bool foundIt = false; AZStd::string watchFolder; AZ::Data::AssetInfo assetInfo; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(foundIt, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, iconPath.toUtf8().constData(), assetInfo, watchFolder); + AssetSystemRequestBus::BroadcastResult( + foundIt, &AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, iconPath.toUtf8().constData(), assetInfo, + watchFolder); if (foundIt) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.cpp index 4869e0d09f..d7eb83a0bb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Thumbnails/SourceThumbnail.cpp @@ -67,7 +67,8 @@ namespace AzToolsFramework bool foundIt = false; AZStd::string watchFolder; AZ::Data::AssetInfo assetInfo; - AssetSystemRequestBus::BroadcastResult(foundIt, &AssetSystemRequestBus::Events::GetSourceInfoBySourceUUID, sourceKey->GetSourceUuid(), assetInfo, watchFolder); + AssetSystemRequestBus::BroadcastResult( + foundIt, &AssetSystemRequestBus::Events::GetSourceInfoBySourceUUID, sourceKey->GetSourceUuid(), assetInfo, watchFolder); QString iconPathToUse; if (foundIt) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp index 93a153cc16..c976f01fde 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.cpp @@ -203,11 +203,6 @@ namespace AzToolsFramework return selectionModel()->selectedIndexes(); } - void AssetBrowserTreeView::SetThumbnailContext(const char* thumbnailContext) const - { - m_delegate->SetThumbnailContext(thumbnailContext); - } - void AssetBrowserTreeView::SetShowSourceControlIcons(bool showSourceControlsIcons) { m_delegate->SetShowSourceControlIcons(showSourceControlsIcons); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h index cae50efe17..ea13c0d2a6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/AssetBrowserTreeView.h @@ -73,7 +73,6 @@ namespace AzToolsFramework void OnAssetBrowserComponentReady() override; ////////////////////////////////////////////////////////////////////////// - void SetThumbnailContext(const char* context) const; void SetShowSourceControlIcons(bool showSourceControlsIcons); void UpdateAfterFilter(bool hasFilter, bool selectFirstValidEntry); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp index 5eca1f457f..c4ce1012c1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.cpp @@ -112,11 +112,6 @@ namespace AzToolsFramework } } - void EntryDelegate::SetThumbnailContext(const char* thumbnailContext) - { - m_thumbnailContext = thumbnailContext; - } - void EntryDelegate::SetShowSourceControlIcons(bool showSourceControl) { m_showSourceControl = showSourceControl; @@ -125,8 +120,8 @@ namespace AzToolsFramework int EntryDelegate::DrawThumbnail(QPainter* painter, const QPoint& point, const QSize& size, Thumbnailer::SharedThumbnailKey thumbnailKey) const { SharedThumbnail thumbnail; - ThumbnailerRequestsBus::BroadcastResult(thumbnail, &ThumbnailerRequests::GetThumbnail, thumbnailKey, m_thumbnailContext.c_str()); - AZ_Assert(thumbnail, "The shared numbernail was not available from the ThumbnailerRequestsBus."); + ThumbnailerRequestBus::BroadcastResult(thumbnail, &ThumbnailerRequests::GetThumbnail, thumbnailKey); + AZ_Assert(thumbnail, "The shared numbernail was not available from the ThumbnailerRequestBus."); AZ_Assert(painter, "A null QPainter was passed in to DrawThumbnail."); if (!painter || !thumbnail || thumbnail->GetState() == Thumbnail::State::Failed) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h index 7c7d919f2d..be7f919462 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/Views/EntryDelegate.h @@ -11,7 +11,6 @@ #if !defined(Q_MOC_RUN) #include #include -#include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: class 'QScopedPointer' needs to have dll-interface to be used by clients of class 'QBrush' // 4800: 'uint': forcing value to bool 'true' or 'false' (performance warning) #include @@ -50,14 +49,11 @@ namespace AzToolsFramework QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; - //! Set location where thumbnails are located for this instance of asset browser - void SetThumbnailContext(const char* thumbnailContext); //! Set whether to show source control icons, this is still temporary mainly to support existing functionality of material browser void SetShowSourceControlIcons(bool showSourceControl); protected: int m_iconSize; - AZStd::string m_thumbnailContext = ThumbnailContext::DefaultContext; bool m_showSourceControl = false; //! Draw a thumbnail and return its width int DrawThumbnail(QPainter* painter, const QPoint& point, const QSize& size, Thumbnailer::SharedThumbnailKey thumbnailKey) const; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.cpp index 7e496390c8..4c08ef6855 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.cpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include namespace AzToolsFramework { @@ -37,20 +39,20 @@ namespace AzToolsFramework void BoxViewportEdit::UpdateManipulators() { AZ::Transform boxWorldFromLocal = AZ::Transform::CreateIdentity(); - BoxManipulatorRequestBus::EventResult( - boxWorldFromLocal, m_entityComponentIdPair, &BoxManipulatorRequests::GetCurrentTransform); + AZ::TransformBus::EventResult( + boxWorldFromLocal, m_entityComponentIdPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); - AZ::Vector3 boxScale = AZ::Vector3::CreateOne(); - BoxManipulatorRequestBus::EventResult( - boxScale, m_entityComponentIdPair, &BoxManipulatorRequests::GetBoxScale); + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult( + nonUniformScale, m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequestBus::Events::GetScale); AZ::Vector3 boxDimensions = AZ::Vector3::CreateZero(); BoxManipulatorRequestBus::EventResult( - boxDimensions, m_entityComponentIdPair, &BoxManipulatorRequests::GetDimensions); + boxDimensions, m_entityComponentIdPair, &BoxManipulatorRequestBus::Events::GetDimensions); - // ensure we apply the entity scale to the box dimensions so - // the manipulators appear in the correct location - boxDimensions *= boxScale; + AZ::Transform boxLocalTransform = AZ::Transform::CreateIdentity(); + BoxManipulatorRequestBus::EventResult( + boxLocalTransform, m_entityComponentIdPair, &BoxManipulatorRequestBus::Events::GetCurrentLocalTransform); for (size_t manipulatorIndex = 0; manipulatorIndex < m_linearManipulators.size(); ++manipulatorIndex) { @@ -58,7 +60,8 @@ namespace AzToolsFramework { linearManipulator->SetSpace(boxWorldFromLocal); linearManipulator->SetLocalTransform( - AZ::Transform::CreateTranslation(s_boxAxes[manipulatorIndex] * 0.5f * boxDimensions)); + boxLocalTransform * AZ::Transform::CreateTranslation(s_boxAxes[manipulatorIndex] * 0.5f * boxDimensions)); + linearManipulator->SetNonUniformScale(nonUniformScale); linearManipulator->SetBoundsDirty(); } } @@ -69,8 +72,8 @@ namespace AzToolsFramework m_entityComponentIdPair = entityComponentIdPair; AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); - BoxManipulatorRequestBus::EventResult( - worldFromLocal, entityComponentIdPair, &BoxManipulatorRequests::GetCurrentTransform); + AZ::TransformBus::EventResult( + worldFromLocal, entityComponentIdPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); for (size_t manipulatorIndex = 0; manipulatorIndex < m_linearManipulators.size(); ++manipulatorIndex) { @@ -85,36 +88,35 @@ namespace AzToolsFramework ManipulatorViews views; views.emplace_back(CreateManipulatorViewQuadBillboard( - AzFramework::ViewportColors::DefaultManipulatorHandleColor, AzFramework::ViewportConstants::DefaultManipulatorHandleSize)); + AzFramework::ViewportColors::DefaultManipulatorHandleColor, + AzFramework::ViewportConstants::DefaultManipulatorHandleSize)); linearManipulator->SetViews(AZStd::move(views)); linearManipulator->InstallMouseMoveCallback( - [this, entityComponentIdPair]( - const LinearManipulator::Action& action) + [this, entityComponentIdPair, + transformScale{ linearManipulator->GetSpace().GetUniformScale() }](const LinearManipulator::Action& action) { + AZ::Transform boxLocalTransform = AZ::Transform::CreateIdentity(); + BoxManipulatorRequestBus::EventResult( + boxLocalTransform, entityComponentIdPair, &BoxManipulatorRequestBus::Events::GetCurrentLocalTransform); + + const AZ::Vector3 manipulatorPosition = GetPositionInManipulatorFrame(transformScale, boxLocalTransform, action); + // calculate the amount of displacement along an axis this manipulator has moved // clamp movement so it cannot go negative based on axis direction const AZ::Vector3 axisDisplacement = - action.LocalPosition().GetAbs() * 2.0f - * AZ::GetMax(0.0f, action.LocalPosition().GetNormalized().Dot(action.m_fixed.m_axis)); - - AZ::Vector3 boxScale = AZ::Vector3::CreateOne(); - BoxManipulatorRequestBus::EventResult( - boxScale, entityComponentIdPair, &BoxManipulatorRequests::GetBoxScale); + manipulatorPosition.GetAbs() * 2.0f + * AZ::GetMax(0.0f, manipulatorPosition.GetNormalized().Dot(action.m_fixed.m_axis)); AZ::Vector3 boxDimensions = AZ::Vector3::CreateZero(); BoxManipulatorRequestBus::EventResult( - boxDimensions, entityComponentIdPair, &BoxManipulatorRequests::GetDimensions); - - // ensure we take into account the entity scale using the axis displacement - const AZ::Vector3 scaledAxisDisplacement = - axisDisplacement / boxScale; + boxDimensions, entityComponentIdPair, &BoxManipulatorRequestBus::Events::GetDimensions); // update dimensions - preserve dimensions not effected by this // axis, and update current axis displacement BoxManipulatorRequestBus::Event( - entityComponentIdPair, &BoxManipulatorRequests::SetDimensions, - (NotAxis(action.m_fixed.m_axis) * boxDimensions).GetMax(scaledAxisDisplacement)); + entityComponentIdPair, &BoxManipulatorRequestBus::Events::SetDimensions, + (NotAxis(action.m_fixed.m_axis) * boxDimensions).GetMax(axisDisplacement)); UpdateManipulators(); }); @@ -137,4 +139,12 @@ namespace AzToolsFramework } } } + + AZ::Vector3 GetPositionInManipulatorFrame(float worldUniformScale, const AZ::Transform& manipulatorLocalTransform, + const LinearManipulator::Action& action) + { + return manipulatorLocalTransform.GetInverse().TransformPoint( + action.m_start.m_localPosition + + action.m_current.m_localPositionOffset / AZ::GetClamp(worldUniformScale, AZ::MinTransformScale, AZ::MaxTransformScale)); + } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.h index 98ef17e547..66ea626e12 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.h @@ -31,4 +31,10 @@ namespace AzToolsFramework using BoxManipulators = AZStd::array, 6>; BoxManipulators m_linearManipulators; ///< Manipulators for editing box size. }; + + /// Calculates the position of the manipulator in its own reference frame. + /// Removes the effects of the manipulator local transform, and accounts for world transform scale in + /// the action local offset. + AZ::Vector3 GetPositionInManipulatorFrame(float worldUniformScale, const AZ::Transform& manipulatorLocalTransform, + const LinearManipulator::Action& action); } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityTransformBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityTransformBus.h index 17739b5098..78ebbb039f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityTransformBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityTransformBus.h @@ -10,10 +10,10 @@ #include +#include + namespace AzToolsFramework { - using EntityIdList = AZStd::vector; - //! Notifications about entity transform changes from the editor. class EditorTransformChangeNotifications : public AZ::EBusTraits { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityTypes.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityTypes.h new file mode 100644 index 0000000000..826fe4417e --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityTypes.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace AzToolsFramework +{ + using EntityIdList = AZStd::vector; + using EntityIdSet = AZStd::unordered_set; + using EntityList = AZStd::vector; + +} // namespace AZ diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h index 2d3db0ea72..8506d32d5b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -97,7 +98,6 @@ namespace AzToolsFramework , private AzFramework::SliceEntityRequestBus::MultiHandler { public: - using EntityList = AzFramework::EntityList; using OnEntitiesAddedCallback = AzFramework::OnEntitiesAddedCallback; using OnEntitiesRemovedCallback = AzFramework::OnEntitiesRemovedCallback; using ValidateEntitiesCallback = AzFramework::ValidateEntitiesCallback; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipService.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipService.h index 1399b46066..4b39a0b263 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipService.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipService.h @@ -9,13 +9,13 @@ #pragma once #include +#include #include #include namespace AzToolsFramework { - using EntityIdSet = AZStd::unordered_set; using EntityIdToEntityIdMap = AZStd::unordered_map; class SliceEditorEntityOwnershipService diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h index 4d7ea86b49..38f81ce6dc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h @@ -8,10 +8,11 @@ #pragma once -#include #include +#include #include #include +#include namespace AZ { @@ -20,9 +21,6 @@ namespace AZ namespace AzToolsFramework { - using EntityIdList = AZStd::vector; - using EntityList = AZStd::vector; - /** * Indicates how an entity was removed from its slice instance, so the said entity can be restored properly. */ diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h index 74d207af64..2a565830bd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h @@ -8,17 +8,17 @@ #pragma once +#include #include #include -#include #include #include +#include + namespace AzToolsFramework { - using EntityIdList = AZStd::vector; - //! FocusModeInterface //! Interface to handle the Editor Focus Mode. class FocusModeInterface diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h index 07a622c936..1b76ca7a85 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h @@ -54,7 +54,8 @@ namespace AzToolsFramework virtual ~BaseManipulator(); - using EntityComponentIds = AZStd::unordered_set; + using UniqueEntityIds = AZStd::unordered_set; + using UniqueEntityComponentIds = AZStd::unordered_set; //! Callback for the event when the mouse pointer is over this manipulator and the left mouse button is pressed. //! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera @@ -137,7 +138,7 @@ namespace AzToolsFramework } //! Returns all EntityComponentIdPairs associated with this manipulator. - const EntityComponentIds& EntityComponentIdPairs() const + const UniqueEntityComponentIds& EntityComponentIdPairs() const { return m_entityComponentIdPairs; } @@ -147,10 +148,10 @@ namespace AzToolsFramework //! Remove an entity from being affected by this manipulator. //! @note All components on this entity registered with the manipulator will be removed. - EntityComponentIds::iterator RemoveEntityId(AZ::EntityId entityId); + UniqueEntityComponentIds::iterator RemoveEntityId(AZ::EntityId entityId); //! Remove a specific component (via a EntityComponentIdPair) being affected by this manipulator. - EntityComponentIds::iterator RemoveEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair); + UniqueEntityComponentIds::iterator RemoveEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair); //! Is this entity currently being tracked by this manipulator. bool HasEntityId(AZ::EntityId entityId) const; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BoxManipulatorRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BoxManipulatorRequestBus.h index b0bcdf47f2..0d6299f5d4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BoxManipulatorRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BoxManipulatorRequestBus.h @@ -26,11 +26,21 @@ namespace AzToolsFramework virtual AZ::Vector3 GetDimensions() = 0; //! Set the X/Y/Z dimensions of the box shape/collider. virtual void SetDimensions(const AZ::Vector3& dimensions) = 0; + // O3DE_DEPRECATION_NOTICE(GHI-7572) + //! @deprecated Because non-uniform scale effects can be complex, it is recommended to separately use + //! AZ::TransformBus::Events::GetWorldTM, AZ::NonUniformScaleRequests::GetScale and GetCurrentLocalTransform + //! and combine their effects. //! Get the transform of the box shape/collider. //! This is used by \ref BoxComponentMode instead of the \ref \AZ::TransformBus //! because a collider may have an additional translation/orientation offset from //! the Entity transform. virtual AZ::Transform GetCurrentTransform() = 0; + //! Get the transform of the box relative to the entity. + virtual AZ::Transform GetCurrentLocalTransform() = 0; + // O3DE_DEPRECATION_NOTICE(GHI-7572) + //! @deprecated Because non-uniform scale effects can be complex, it is recommended to separately use + //! AZ::TransformBus::Events::GetWorldTM, AZ::NonUniformScaleRequests::GetScale and GetCurrentLocalTransform + //! and combine their effects. //! Get the scale currently applied to the box. //! With the Box Shape, the largest x/y/z component is taken //! so scale is always uniform, with colliders the scale may diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.cpp index 58d579b20c..7de1703769 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.cpp @@ -80,23 +80,18 @@ namespace AzToolsFramework m_onMouseMoveCallback = onMouseMoveCallback; } + void SurfaceManipulator::InstallEntityIdsToIgnoreFn(EntityIdsToIgnoreFn entityIdsToIgnoreCallback) + { + m_entityIdsToIgnoreFn = AZStd::move(entityIdsToIgnoreCallback); + } + void SurfaceManipulator::OnLeftMouseDownImpl( const ViewportInteraction::MouseInteraction& interaction, [[maybe_unused]] float rayIntersectionDistance) { const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); - const AzFramework::ViewportId viewportId = interaction.m_interactionId.m_viewportId; - const auto& entityComponentIdPairs = EntityComponentIdPairs(); - m_rayRequest.m_entityFilter.m_ignoreEntities.clear(); - m_rayRequest.m_entityFilter.m_ignoreEntities.reserve(entityComponentIdPairs.size()); - AZStd::transform( - entityComponentIdPairs.begin(), entityComponentIdPairs.end(), - AZStd::inserter(m_rayRequest.m_entityFilter.m_ignoreEntities, m_rayRequest.m_entityFilter.m_ignoreEntities.begin()), - [](const AZ::EntityComponentIdPair& entityComponentIdPair) - { - return entityComponentIdPair.GetEntityId(); - }); + m_rayRequest.m_entityFilter.m_ignoreEntities = m_entityIdsToIgnoreFn(interaction); // calculate the start and end of the ray RefreshRayRequest( @@ -139,6 +134,8 @@ namespace AzToolsFramework { const AzFramework::ViewportId viewportId = interaction.m_interactionId.m_viewportId; + m_rayRequest.m_entityFilter.m_ignoreEntities = m_entityIdsToIgnoreFn(interaction); + // update the start and end of the ray RefreshRayRequest( m_rayRequest, ViewportInteraction::ViewportScreenToWorldRay(viewportId, interaction.m_mousePick.m_screenCoordinates), @@ -162,12 +159,12 @@ namespace AzToolsFramework const ManipulatorManagerState& managerState, AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, - const ViewportInteraction::MouseInteraction& mouseInteraction) + const ViewportInteraction::MouseInteraction& interaction) { m_manipulatorView->Draw( GetManipulatorManagerId(), managerState, GetManipulatorId(), ManipulatorState{ TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalPosition(), MouseOver() }, debugDisplay, - cameraState, mouseInteraction); + cameraState, interaction); } void SurfaceManipulator::InvalidateImpl() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.h index 091a71d8da..314ad7b253 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.h @@ -40,6 +40,9 @@ namespace AzToolsFramework //! A Manipulator must only be created and managed through a shared_ptr. static AZStd::shared_ptr MakeShared(const AZ::Transform& worldFromLocal); + //! Callback function to determine which EntityIds to ignore when performing the ray intersection. + using EntityIdsToIgnoreFn = AZStd::function; + //! The state of the manipulator at the start of an interaction. struct Start { @@ -77,11 +80,13 @@ namespace AzToolsFramework void InstallLeftMouseUpCallback(const MouseActionCallback& onMouseUpCallback); void InstallMouseMoveCallback(const MouseActionCallback& onMouseMoveCallback); + void InstallEntityIdsToIgnoreFn(EntityIdsToIgnoreFn entityIdsToIgnoreFn); + void Draw( const ManipulatorManagerState& managerState, AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, - const ViewportInteraction::MouseInteraction& mouseInteraction) override; + const ViewportInteraction::MouseInteraction& interaction) override; void SetView(AZStd::unique_ptr&& view); @@ -109,6 +114,9 @@ namespace AzToolsFramework MouseActionCallback m_onLeftMouseUpCallback = nullptr; MouseActionCallback m_onMouseMoveCallback = nullptr; + //! Customization point to determine which (if any) EntityIds to ignore while performing the ray intersection. + EntityIdsToIgnoreFn m_entityIdsToIgnoreFn = nullptr; + //! Cached ray request initialized at mouse down and updated during mouse move. AzFramework::RenderGeometry::RayRequest m_rayRequest; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp index 5810662e57..c6e9403f5f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp @@ -145,6 +145,15 @@ namespace AzToolsFramework } } + void TranslationManipulators::InstallSurfaceManipulatorEntityIdsToIgnoreFn( + SurfaceManipulator::EntityIdsToIgnoreFn entityIdsToIgnoreFn) + { + if (m_surfaceManipulator) + { + m_surfaceManipulator->InstallEntityIdsToIgnoreFn(AZStd::move(entityIdsToIgnoreFn)); + } + } + void TranslationManipulators::SetLocalTransformImpl(const AZ::Transform& localTransform) { for (AZStd::shared_ptr& manipulator : m_linearManipulators) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.h index ea8dbb7975..6c841d16f6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.h @@ -61,6 +61,8 @@ namespace AzToolsFramework void InstallSurfaceManipulatorMouseMoveCallback(const SurfaceManipulator::MouseActionCallback& onMouseMoveCallback); void InstallSurfaceManipulatorMouseUpCallback(const SurfaceManipulator::MouseActionCallback& onMouseUpCallback); + void InstallSurfaceManipulatorEntityIdsToIgnoreFn(SurfaceManipulator::EntityIdsToIgnoreFn entityIdsToIgnoreFn); + void SetSpaceImpl(const AZ::Transform& worldFromLocal) override; void SetLocalTransformImpl(const AZ::Transform& localTransform) override; void SetLocalPositionImpl(const AZ::Vector3& localPosition) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h index ebc75dc5cf..64c7cb3d2a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h @@ -18,6 +18,7 @@ #include #include #include +#include #include namespace AZ @@ -62,7 +63,6 @@ namespace AzToolsFramework using AliasToInstanceMap = AZStd::unordered_map>; using AliasToEntityMap = AZStd::unordered_map>; - using EntityList = AZStd::vector; Instance(); explicit Instance(AZStd::unique_ptr containerEntity); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceEntityScrubber.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceEntityScrubber.cpp index ca0bb829ca..e62fc710fc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceEntityScrubber.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceEntityScrubber.cpp @@ -12,7 +12,7 @@ namespace AzToolsFramework { namespace Prefab { - InstanceEntityScrubber::InstanceEntityScrubber(Instance::EntityList& entities) + InstanceEntityScrubber::InstanceEntityScrubber(EntityList& entities) : m_entities(entities) {} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceEntityScrubber.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceEntityScrubber.h index 394261e902..4124811795 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceEntityScrubber.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceEntityScrubber.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace AZ @@ -19,8 +20,6 @@ namespace AZ namespace AzToolsFramework { - using EntityList = AZStd::vector; - namespace Prefab { //! Collects the entities added during deserialization @@ -29,12 +28,12 @@ namespace AzToolsFramework public: AZ_TYPE_INFO(InstanceEntityScrubber, "{0BC12562-C240-48AD-89C6-EDF572C9B485}"); - explicit InstanceEntityScrubber(Instance::EntityList& entities); + explicit InstanceEntityScrubber(EntityList& entities); void AddEntitiesToScrub(const EntityList& entities); private: - Instance::EntityList& m_entities; + EntityList& m_entities; }; } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp index ea1af5cccb..336ff32498 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp @@ -154,7 +154,7 @@ namespace AzToolsFramework continue; } - Instance::EntityList newEntities; + EntityList newEntities; // Climb up to the root of the instance hierarchy from this instance InstanceOptionalConstReference rootInstance = *instanceToUpdate; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp index d1d3491156..4ca1979d81 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp @@ -284,7 +284,7 @@ namespace AzToolsFramework } bool LoadInstanceFromPrefabDom( - Instance& instance, Instance::EntityList& newlyAddedEntities, const PrefabDom& prefabDom, LoadFlags flags) + Instance& instance, EntityList& newlyAddedEntities, const PrefabDom& prefabDom, LoadFlags flags) { // When entities are rebuilt they are first destroyed. As a result any assets they were exclusively holding on to will // be released and reloaded once the entities are built up again. By suspending asset release temporarily the asset reload diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.h index d2234f423b..fe58416788 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.h @@ -133,7 +133,7 @@ namespace AzToolsFramework * @return bool on whether the operation succeeded. */ bool LoadInstanceFromPrefabDom( - Instance& instance, Instance::EntityList& newlyAddedEntities, const PrefabDom& prefabDom, + Instance& instance, EntityList& newlyAddedEntities, const PrefabDom& prefabDom, LoadFlags flags = LoadFlags::None); inline PrefabDomPath GetPrefabDomInstancePath(const char* instanceName) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index a1a7ac9844..4b6a6dd039 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -22,8 +23,6 @@ class QString; namespace AzToolsFramework { - using EntityList = AZStd::vector; - namespace Prefab { class Instance; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h index 2eb0bfa8e6..bab6da0f3f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicInterface.h @@ -13,10 +13,10 @@ #include #include +#include + namespace AzToolsFramework { - using EntityIdList = AZStd::vector; - namespace UndoSystem { class URSequencePoint; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestBus.h index cf7665bdd7..779d5af9c6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestBus.h @@ -9,7 +9,6 @@ #pragma once #include -#include #include #include #include @@ -18,10 +17,10 @@ #include #include +#include + namespace AzToolsFramework { - using EntityIdList = AZStd::vector; - namespace Prefab { using CreatePrefabResult = AZ::Outcome; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index 3234aa8b2d..8b246161b2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -319,7 +319,7 @@ namespace AzToolsFramework } auto newInstance = AZStd::make_unique(parent); - Instance::EntityList newEntities; + EntityList newEntities; if (!PrefabDomUtils::LoadInstanceFromPrefabDom(*newInstance, newEntities, instantiatingTemplate->get().GetPrefabDom())) { AZ_Error("Prefab", false, diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h index b547e9bdd2..e52da2127a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -36,8 +37,6 @@ namespace AZ namespace AzToolsFramework { - using EntityList = AZStd::vector; - namespace Prefab { using InstanceList = AZStd::vector; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.h index dbad58d2f5..b1bc7131b5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -50,7 +51,6 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils static void Reflect(AZ::ReflectContext* context); protected: - using EntityList = AZStd::vector; static void GetEntitiesFromInstance(AzToolsFramework::Prefab::Instance& instance, EntityList& hierarchyEntities); static bool ReadComponentAttribute( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/Thumbnail.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/Thumbnail.cpp index 0762a07493..d6e39a5027 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/Thumbnail.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/Thumbnail.cpp @@ -23,7 +23,15 @@ namespace AzToolsFramework ////////////////////////////////////////////////////////////////////////// // ThumbnailKey ////////////////////////////////////////////////////////////////////////// - bool ThumbnailKey::IsReady() const { return m_ready; } + void ThumbnailKey::SetReady(bool ready) + { + m_ready = ready; + } + + bool ThumbnailKey::IsReady() const + { + return m_ready; + } bool ThumbnailKey::UpdateThumbnail() { @@ -75,10 +83,8 @@ namespace AzToolsFramework if (m_state == State::Unloaded) { m_state = State::Loading; - QThreadPool* threadPool; - ThumbnailContextRequestBus::BroadcastResult( - threadPool, - &ThumbnailContextRequestBus::Handler::GetThreadPool); + QThreadPool* threadPool = {}; + ThumbnailerRequestBus::BroadcastResult(threadPool, &ThumbnailerRequestBus::Handler::GetThreadPool); QFuture future = QtConcurrent::run(threadPool, [this](){ LoadThread(); }); m_watcher.setFuture(future); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/Thumbnail.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/Thumbnail.h index d3351959db..e76e90cec8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/Thumbnail.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/Thumbnail.h @@ -26,13 +26,11 @@ namespace AzToolsFramework //! ThumbnailKey is used to locate thumbnails in thumbnail cache /* ThumbnailKey contains any kind of identifiable information to retrieve thumbnails (e.g. assetId, assetType, filename, etc.) - To use thumbnail system, keep reference to your thumbnail key, and retrieve Thumbnail via ThumbnailerRequestsBus + To use thumbnail system, keep reference to your thumbnail key, and retrieve Thumbnail via ThumbnailerRequestBus */ class ThumbnailKey : public QObject { - friend class ThumbnailContext; - Q_OBJECT public: AZ_RTTI(ThumbnailKey, "{43F20F6B-333D-4226-8E4F-331A62315255}"); @@ -40,6 +38,8 @@ namespace AzToolsFramework ThumbnailKey() = default; virtual ~ThumbnailKey() = default; + void SetReady(bool ready); + bool IsReady() const; virtual bool UpdateThumbnail(); @@ -47,13 +47,13 @@ namespace AzToolsFramework virtual size_t GetHash() const; virtual bool Equals(const ThumbnailKey* other) const; + Q_SIGNALS: //! Updated signal is dispatched whenever thumbnail data was changed. Anyone using this thumbnail should listen to this. void ThumbnailUpdatedSignal() const; //! Force update mapped thumbnails void UpdateThumbnailSignal() const; - private: bool m_ready = false; }; @@ -114,11 +114,14 @@ namespace AzToolsFramework ThumbnailProvider() = default; virtual ~ThumbnailProvider() = default; virtual bool GetThumbnail(SharedThumbnailKey key, SharedThumbnail& thumbnail) = 0; - //! Priority identifies ThumbnailProvider order in ThumbnailContext - //! Higher priority means this ThumbnailProvider will take precedence in generating a thumbnail when - //! a supplied ThumbnailKey is supported by multiple providers. - virtual int GetPriority() const { return 0; } - //! A unique ThumbnailProvider name identifying it in a ThumbnailContext + //! Priority identifies ThumbnailProvider order + //! Higher priority means this ThumbnailProvider will take precedence in generating a thumbnail when a supplied ThumbnailKey is + //! supported by multiple providers. + virtual int GetPriority() const + { + return 0; + } + //! A unique ThumbnailProvider name identifyier virtual const char* GetProviderName() const = 0; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailContext.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailContext.cpp deleted file mode 100644 index cd2c673a24..0000000000 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailContext.cpp +++ /dev/null @@ -1,132 +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 - -AZ_PUSH_DISABLE_WARNING(4244 4251, "-Wunknown-warning-option") // 4251: 'QImageIOHandler::d_ptr': class 'QScopedPointer>' needs to have dll-interface to be used by clients of class 'QImageIOHandler' -#include -AZ_POP_DISABLE_WARNING - -namespace AzToolsFramework -{ - namespace Thumbnailer - { - ThumbnailContext::ThumbnailContext() - : m_missingThumbnail(new MissingThumbnail()) - , m_loadingThumbnail(new LoadingThumbnail()) - , m_threadPool(this) - { - ThumbnailContextRequestBus::Handler::BusConnect(); - } - - ThumbnailContext::~ThumbnailContext() - { - ThumbnailContextRequestBus::Handler::BusDisconnect(); - } - - bool ThumbnailContext::IsLoading(SharedThumbnailKey key) - { - SharedThumbnail thumbnail; - - for (auto& provider : m_providers) - { - if (provider->GetThumbnail(key, thumbnail)) - { - return thumbnail->GetState() == Thumbnail::State::Unloaded || - thumbnail->GetState() == Thumbnail::State::Loading; - } - } - return false; - } - - void ThumbnailContext::RedrawThumbnail() - { - AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Broadcast(&AzToolsFramework::AssetBrowser::AssetBrowserViewRequests::Update); - } - - QThreadPool* ThumbnailContext::GetThreadPool() - { - return &m_threadPool; - } - - SharedThumbnail ThumbnailContext::GetThumbnail(SharedThumbnailKey key) - { - SharedThumbnail thumbnail; - // find provider who can handle supplied key - for (auto& provider : m_providers) - { - if (provider->GetThumbnail(key, thumbnail)) - { - // if thumbnail is ready return it - if (thumbnail->GetState() == Thumbnail::State::Ready) - { - return thumbnail; - } - // if thumbnail is not loaded, start loading it, meanwhile return loading thumbnail - if (thumbnail->GetState() == Thumbnail::State::Unloaded) - { - // listen to the loading signal, so the anyone using it will update loading animation - connect(m_loadingThumbnail.data(), &Thumbnail::Updated, key.data(), &ThumbnailKey::ThumbnailUpdatedSignal); - AzQtComponents::StyledBusyLabel* busyLabel; - AzToolsFramework::AssetBrowser::AssetBrowserComponentRequestBus::BroadcastResult(busyLabel, &AzToolsFramework::AssetBrowser::AssetBrowserComponentRequests::GetStyledBusyLabel); - connect(busyLabel, &AzQtComponents::StyledBusyLabel::repaintNeeded, this, &ThumbnailContext::RedrawThumbnail); - // once the thumbnail is loaded, disconnect it from loading thumbnail - connect(thumbnail.data(), &Thumbnail::Updated, this , [this, key, thumbnail, busyLabel]() - { - disconnect(m_loadingThumbnail.data(), &Thumbnail::Updated, key.data(), &ThumbnailKey::ThumbnailUpdatedSignal); - disconnect(busyLabel, &AzQtComponents::StyledBusyLabel::repaintNeeded, this, &ThumbnailContext::RedrawThumbnail); - thumbnail->disconnect(); - connect(thumbnail.data(), &Thumbnail::Updated, key.data(), &ThumbnailKey::ThumbnailUpdatedSignal); - connect(key.data(), &ThumbnailKey::UpdateThumbnailSignal, thumbnail.data(), &Thumbnail::Update); - key->m_ready = true; - Q_EMIT key->ThumbnailUpdatedSignal(); - }); - thumbnail->Load(); - } - if (thumbnail->GetState() == Thumbnail::State::Failed) - { - return m_missingThumbnail; - } - return m_loadingThumbnail; - } - } - return m_missingThumbnail; - } - - void ThumbnailContext::RegisterThumbnailProvider(SharedThumbnailProvider providerToAdd) - { - auto it = AZStd::find_if(m_providers.begin(), m_providers.end(), [providerToAdd](const SharedThumbnailProvider& provider) - { - return AZ::StringFunc::Equal(provider->GetProviderName(), providerToAdd->GetProviderName()); - }); - - if (it != m_providers.end()) - { - AZ_Error("ThumbnailContext", false, "Provider with name %s is already registered with context.", providerToAdd->GetProviderName()); - return; - } - - m_providers.insert(providerToAdd); - } - - void ThumbnailContext::UnregisterThumbnailProvider(const char* providerName) - { - auto it = AZStd::remove_if(m_providers.begin(), m_providers.end(), [providerName](const SharedThumbnailProvider& provider) - { - return AZ::StringFunc::Equal(provider->GetProviderName(), providerName); - }); - m_providers.erase(it, m_providers.end()); - } - } // namespace Thumbnailer -} // namespace AzToolsFramework - -#include "Thumbnails/moc_ThumbnailContext.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailContext.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailContext.h deleted file mode 100644 index 77d56b7831..0000000000 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailContext.h +++ /dev/null @@ -1,86 +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 - -#if !defined(Q_MOC_RUN) -#include -#include -#include -#include - -#include -#include -#include -#endif - -class QString; -class QPixmap; - -namespace AzToolsFramework -{ - namespace Thumbnailer - { - class ThumbnailProvider; - - //! ThumbnailContext provides distinct thumbnail location for specific context - /* - There can be any number of contexts for every unique feature that may need different types of thumbnails. - For example 'AssetBrowser' context provides thumbnails specific to Asset Browser - 'PreviewContext' may provide thumbnails for Preview Widget - 'MaterialBrowser' may provide thumbnails for Material Browser - etc. - */ - class ThumbnailContext - : public QObject - , public ThumbnailContextRequestBus::Handler - { - Q_OBJECT - public: - AZ_CLASS_ALLOCATOR(ThumbnailContext, AZ::SystemAllocator, 0); - - ThumbnailContext(); - ~ThumbnailContext() override; - - //! Is the thumbnail currently loading or is about to load. - bool IsLoading(SharedThumbnailKey key); - //! Retrieve thumbnail by key, generate one if needed - SharedThumbnail GetThumbnail(SharedThumbnailKey key); - //! Add new thumbnail cache - void RegisterThumbnailProvider(SharedThumbnailProvider providerToAdd); - //! Remove thumbnail cache by name if found - void UnregisterThumbnailProvider(const char* providerName); - - void RedrawThumbnail(); - - //! Default context used for most thumbnails - static constexpr const char* DefaultContext = "Default"; - - // ThumbnailContextRequestBus::Handler interface overrides... - QThreadPool* GetThreadPool() override; - - private: - struct ProviderCompare { - bool operator() (const SharedThumbnailProvider& lhs, const SharedThumbnailProvider& rhs) const - { - // sorting in reverse, higher priority means the provider should be considered first - return lhs->GetPriority() > rhs->GetPriority(); - } - }; - - //! Collection of thumbnail caches provided by this context - AZStd::multiset m_providers; - //! Default missing thumbnail used when no thumbnail for given key can be found within this context - SharedThumbnail m_missingThumbnail; - //! Default loading thumbnail used when thumbnail is found by is not yet generated - SharedThumbnail m_loadingThumbnail; - //! There is only a limited number of threads on global threadPool, because there can be many thumbnails rendering at once - //! an individual threadPool is needed to avoid deadlocks - QThreadPool m_threadPool; - }; - } // namespace Thumbnailer -} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailWidget.cpp index 38bd854f17..e7c31fd1e1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailWidget.cpp @@ -30,14 +30,13 @@ namespace AzToolsFramework { } - void ThumbnailWidget::SetThumbnailKey(SharedThumbnailKey key, const char* contextName) + void ThumbnailWidget::SetThumbnailKey(SharedThumbnailKey key) { if (m_key) { disconnect(m_key.data(), &ThumbnailKey::ThumbnailUpdatedSignal, this, &ThumbnailWidget::KeyUpdatedSlot); } m_key = key; - m_contextName = contextName; connect(m_key.data(), &ThumbnailKey::ThumbnailUpdatedSignal, this, &ThumbnailWidget::KeyUpdatedSlot); repaint(); } @@ -65,7 +64,7 @@ namespace AzToolsFramework { // thumbnail instance is not stored locally, but retrieved each paintEvent since thumbnail mapped to a specific key may change SharedThumbnail thumbnail; - ThumbnailerRequestsBus::BroadcastResult(thumbnail, &ThumbnailerRequests::GetThumbnail, m_key, m_contextName.c_str()); + ThumbnailerRequestBus::BroadcastResult(thumbnail, &ThumbnailerRequests::GetThumbnail, m_key); QPainter painter(this); // Scaling and centering pixmap within bounds to preserve aspect ratio diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailWidget.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailWidget.h index 6dab575af5..a916673702 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailWidget.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailWidget.h @@ -32,7 +32,7 @@ namespace AzToolsFramework ~ThumbnailWidget() override = default; //! Call this to set what thumbnail widget will display - void SetThumbnailKey(SharedThumbnailKey key, const char* contextName = "Default"); + void SetThumbnailKey(SharedThumbnailKey key); //! Remove current thumbnail void ClearThumbnail(); @@ -44,7 +44,6 @@ namespace AzToolsFramework private: SharedThumbnailKey m_key; - AZStd::string m_contextName; private Q_SLOTS: void KeyUpdatedSlot(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerBus.h index acd8ba3966..75aa21af2d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerBus.h @@ -19,48 +19,30 @@ namespace AzToolsFramework { namespace Thumbnailer { - //! Interaction with thumbnail context - class ThumbnailContextRequests - : public AZ::EBusTraits - { - public: - //! Get thread pool for drawing thumbnails - virtual QThreadPool* GetThreadPool() = 0; - }; - - using ThumbnailContextRequestBus = AZ::EBus; - //! Interaction with thumbnailer class ThumbnailerRequests : public AZ::EBusTraits { public: - //! Add thumbnail context - virtual void RegisterContext(const char* contextName) = 0; - - //! Remove thumbnail context and all associated ThumbnailProviders - virtual void UnregisterContext(const char* contextName) = 0; - - //! Return whether a given ThumbnailContext has been registered - virtual bool HasContext(const char* contextName) const = 0; - - //! Add new thumbnail provider to ThumbnailContext - virtual void RegisterThumbnailProvider(SharedThumbnailProvider provider, const char* contextName) = 0; + //! Add new thumbnail provider + virtual void RegisterThumbnailProvider(SharedThumbnailProvider provider) = 0; - //! Remove thumbnail provider from ThumbnailContext - virtual void UnregisterThumbnailProvider(const char* providerName, const char* contextName) = 0; + //! Remove thumbnail provider + virtual void UnregisterThumbnailProvider(const char* providerName) = 0; //! Retrieve thumbnail by key, //! if no thumbnail matching found, one of ThumbnailProviders will attempt to create //! If no compatible providers found, MissingThumbnail will be returned - virtual SharedThumbnail GetThumbnail(SharedThumbnailKey thumbnailKey, const char* contextName) = 0; + virtual SharedThumbnail GetThumbnail(SharedThumbnailKey thumbnailKey) = 0; //! Return whether the thumbnail is loading. - virtual bool IsLoading(SharedThumbnailKey thumbnailKey, const char* contextName) = 0; + virtual bool IsLoading(SharedThumbnailKey thumbnailKey) = 0; + + //! Get thread pool for drawing thumbnails + virtual QThreadPool* GetThreadPool() = 0; }; using ThumbnailerRequestBus = AZ::EBus; - using ThumbnailerRequestsBus = AZ::EBus; //deprecated //! Request product thumbnail to be rendered class ThumbnailerRendererRequests diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerComponent.cpp index 170785b631..50504d3652 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerComponent.cpp @@ -6,11 +6,14 @@ * */ -#include - #include +#include +#include +#include +#include +#include +#include #include -#include #include #include @@ -19,8 +22,10 @@ namespace AzToolsFramework { namespace Thumbnailer { - ThumbnailerComponent::ThumbnailerComponent() + : m_missingThumbnail(new MissingThumbnail()) + , m_loadingThumbnail(new LoadingThumbnail()) + , m_threadPool(this) { } @@ -28,14 +33,13 @@ namespace AzToolsFramework void ThumbnailerComponent::Activate() { - RegisterContext(ThumbnailContext::DefaultContext); - BusConnect(); + ThumbnailerRequestBus::Handler::BusConnect(); } void ThumbnailerComponent::Deactivate() { - BusDisconnect(); - m_thumbnails.clear(); + ThumbnailerRequestBus::Handler::BusDisconnect(); + m_providers.clear(); } void ThumbnailerComponent::Reflect(AZ::ReflectContext* context) @@ -49,59 +53,114 @@ namespace AzToolsFramework void ThumbnailerComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { - incompatible.push_back(AZ_CRC("ThumbnailerService", 0x65422b97)); + incompatible.push_back(AZ_CRC_CE("ThumbnailerService")); } void ThumbnailerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { - provided.push_back(AZ_CRC("ThumbnailerService", 0x65422b97)); + provided.push_back(AZ_CRC_CE("ThumbnailerService")); } - void ThumbnailerComponent::RegisterContext(const char* contextName) + void ThumbnailerComponent::RegisterThumbnailProvider(SharedThumbnailProvider provider) { - AZ_Assert(m_thumbnails.find(contextName) == m_thumbnails.end(), "Context %s already registered", contextName); - m_thumbnails[contextName] = AZStd::make_shared(); - } + auto it = AZStd::find_if(m_providers.begin(), m_providers.end(), [provider](const SharedThumbnailProvider& existingProvider) + { + return AZ::StringFunc::Equal(provider->GetProviderName(), existingProvider->GetProviderName()); + }); - void ThumbnailerComponent::UnregisterContext(const char* contextName) - { - AZ_Assert(m_thumbnails.find(contextName) != m_thumbnails.end(), "Context %s not registered", contextName); - m_thumbnails.erase(contextName); + if (it != m_providers.end()) + { + AZ_Error("ThumbnailerComponent", false, "Provider with name %s is already registered with context.", provider->GetProviderName()); + return; + } + + m_providers.insert(provider); } - bool ThumbnailerComponent::HasContext(const char* contextName) const + void ThumbnailerComponent::UnregisterThumbnailProvider(const char* providerName) { - return m_thumbnails.find(contextName) != m_thumbnails.end(); + AZStd::erase_if( + m_providers, + [providerName](const SharedThumbnailProvider& provider) + { + return AZ::StringFunc::Equal(provider->GetProviderName(), providerName); + }); + } - void ThumbnailerComponent::RegisterThumbnailProvider(SharedThumbnailProvider provider, const char* contextName) + SharedThumbnail ThumbnailerComponent::GetThumbnail(SharedThumbnailKey key) { - auto it = m_thumbnails.find(contextName); - AZ_Assert(it != m_thumbnails.end(), "Context %s not registered", contextName); - it->second->RegisterThumbnailProvider(provider); + // find provider who can handle supplied key + for (auto& provider : m_providers) + { + SharedThumbnail thumbnail; + if (provider->GetThumbnail(key, thumbnail)) + { + // if thumbnail is ready return it + if (thumbnail->GetState() == Thumbnail::State::Ready) + { + return thumbnail; + } + + // if thumbnail is not loaded, start loading it, meanwhile return loading thumbnail + if (thumbnail->GetState() == Thumbnail::State::Unloaded) + { + // listen to the loading signal, so the anyone using it will update loading animation + AzQtComponents::StyledBusyLabel* busyLabel; + AssetBrowser::AssetBrowserComponentRequestBus::BroadcastResult(busyLabel, &AssetBrowser::AssetBrowserComponentRequests::GetStyledBusyLabel); + QObject::connect(m_loadingThumbnail.data(), &Thumbnail::Updated, key.data(), &ThumbnailKey::ThumbnailUpdatedSignal); + QObject::connect(busyLabel, &AzQtComponents::StyledBusyLabel::repaintNeeded, this, &ThumbnailerComponent::RedrawThumbnail); + + // once the thumbnail is loaded, disconnect it from loading thumbnail + QObject::connect(thumbnail.data(), &Thumbnail::Updated, this , [this, key, thumbnail, busyLabel]() + { + QObject::disconnect(m_loadingThumbnail.data(), &Thumbnail::Updated, key.data(), &ThumbnailKey::ThumbnailUpdatedSignal); + QObject::disconnect(busyLabel, &AzQtComponents::StyledBusyLabel::repaintNeeded, this, &ThumbnailerComponent::RedrawThumbnail); + + thumbnail->disconnect(); + QObject::connect(thumbnail.data(), &Thumbnail::Updated, key.data(), &ThumbnailKey::ThumbnailUpdatedSignal); + QObject::connect(key.data(), &ThumbnailKey::UpdateThumbnailSignal, thumbnail.data(), &Thumbnail::Update); + + key->SetReady(true); + Q_EMIT key->ThumbnailUpdatedSignal(); + }); + + thumbnail->Load(); + } + + if (thumbnail->GetState() == Thumbnail::State::Failed) + { + return m_missingThumbnail; + } + + return m_loadingThumbnail; + } + } + return m_missingThumbnail; } - void ThumbnailerComponent::UnregisterThumbnailProvider(const char* providerName, const char* contextName) + bool ThumbnailerComponent::IsLoading(SharedThumbnailKey key) { - auto it = m_thumbnails.find(contextName); - AZ_Assert(it != m_thumbnails.end(), "Context %s not registered", contextName); - it->second->UnregisterThumbnailProvider(providerName); + for (auto& provider : m_providers) + { + SharedThumbnail thumbnail; + if (provider->GetThumbnail(key, thumbnail)) + { + return thumbnail->GetState() == Thumbnail::State::Unloaded || thumbnail->GetState() == Thumbnail::State::Loading; + } + } + return false; } - SharedThumbnail ThumbnailerComponent::GetThumbnail(SharedThumbnailKey key, const char* contextName) + QThreadPool* ThumbnailerComponent::GetThreadPool() { - auto it = m_thumbnails.find(contextName); - AZ_Assert(it != m_thumbnails.end(), "Context %s not registered", contextName); - return it->second->GetThumbnail(key); + return &m_threadPool; } - bool ThumbnailerComponent::IsLoading(SharedThumbnailKey key, const char* contextName) + void ThumbnailerComponent::RedrawThumbnail() { - auto it = m_thumbnails.find(contextName); - AZ_Assert(it != m_thumbnails.end(), "Context %s not registered", contextName); - return it->second->IsLoading(key); + AssetBrowser::AssetBrowserViewRequestBus::Broadcast(&AssetBrowser::AssetBrowserViewRequests::Update); } - } // namespace Thumbnailer } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerComponent.h index bd075e28da..33184fa748 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerComponent.h @@ -7,19 +7,29 @@ */ #pragma once -#include +#if !defined(Q_MOC_RUN) #include +#include +#include +#include #include +#include +#include +#include +#endif + +class QString; +class QPixmap; + namespace AzToolsFramework { namespace Thumbnailer { - class ThumbnailContext; - class ThumbnailerComponent : public AZ::Component - , public ThumbnailerRequestsBus::Handler + , public ThumbnailerRequestBus::Handler + , public QObject { public: AZ_COMPONENT(ThumbnailerComponent, "{80090CA5-6A3A-4554-B5FE-A6D74ECB2D84}") @@ -27,28 +37,41 @@ namespace AzToolsFramework ThumbnailerComponent(); virtual ~ThumbnailerComponent(); - ////////////////////////////////////////////////////////////////////////// - // AZ::Component - ////////////////////////////////////////////////////////////////////////// + // AZ::Component overrides... void Activate() override; void Deactivate() override; static void Reflect(AZ::ReflectContext* context); static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); - ////////////////////////////////////////////////////////////////////////// - // ThumbnailerRequests - ////////////////////////////////////////////////////////////////////////// - void RegisterContext(const char* contextName) override; - void UnregisterContext(const char* contextName) override; - bool HasContext(const char* contextName) const override; - void RegisterThumbnailProvider(SharedThumbnailProvider provider, const char* contextName) override; - void UnregisterThumbnailProvider(const char* providerName, const char* contextName) override; - SharedThumbnail GetThumbnail(SharedThumbnailKey thumbnailKey, const char* contextName) override; - bool IsLoading(SharedThumbnailKey thumbnailKey, const char* contextName) override; + // ThumbnailerRequestBus::Handler interface overrides... + void RegisterThumbnailProvider(SharedThumbnailProvider provider) override; + void UnregisterThumbnailProvider(const char* providerName) override; + SharedThumbnail GetThumbnail(SharedThumbnailKey thumbnailKey) override; + bool IsLoading(SharedThumbnailKey thumbnailKey) override; + QThreadPool* GetThreadPool() override; + + void RedrawThumbnail(); private: - AZStd::unordered_map> m_thumbnails; + struct ProviderCompare + { + bool operator()(const SharedThumbnailProvider& lhs, const SharedThumbnailProvider& rhs) const + { + // sorting in reverse, higher priority means the provider should be considered first + return lhs->GetPriority() > rhs->GetPriority(); + } + }; + + //! Collection of thumbnail caches provided by this context + AZStd::multiset m_providers; + //! Default missing thumbnail used when no thumbnail for given key can be found within this context + SharedThumbnail m_missingThumbnail; + //! Default loading thumbnail used when thumbnail is found by is not yet generated + SharedThumbnail m_loadingThumbnail; + //! There is only a limited number of threads on global threadPool, because there can be many thumbnails rendering at once + //! an individual threadPool is needed to avoid deadlocks + QThreadPool m_threadPool; }; } // Thumbnailer } // namespace AssetBrowser diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerNullComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerNullComponent.cpp index 2be634d379..e2d40030c3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerNullComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerNullComponent.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include namespace AzToolsFramework @@ -47,35 +46,27 @@ namespace AzToolsFramework services.push_back(AZ_CRC("ThumbnailerService", 0x65422b97)); } - void ThumbnailerNullComponent::RegisterContext(const char* /*contextName*/) + void ThumbnailerNullComponent::RegisterThumbnailProvider(AzToolsFramework::Thumbnailer::SharedThumbnailProvider /*provider*/) { } - void ThumbnailerNullComponent::UnregisterContext(const char* /*contextName*/) + void ThumbnailerNullComponent::UnregisterThumbnailProvider(const char* /*providerName*/) { } - bool ThumbnailerNullComponent::HasContext(const char* /*contextName*/) const - { - return false; - } - - void ThumbnailerNullComponent::RegisterThumbnailProvider(AzToolsFramework::Thumbnailer::SharedThumbnailProvider /*provider*/, const char* /*contextName*/) - { - } - - void ThumbnailerNullComponent::UnregisterThumbnailProvider(const char* /*providerName*/, const char* /*contextName*/) + AzToolsFramework::Thumbnailer::SharedThumbnail ThumbnailerNullComponent::GetThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey /*key*/) { + return m_nullThumbnail; } - AzToolsFramework::Thumbnailer::SharedThumbnail ThumbnailerNullComponent::GetThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey /*key*/, const char* /*contextName*/) + bool ThumbnailerNullComponent::IsLoading(AzToolsFramework::Thumbnailer::SharedThumbnailKey /*thumbnailKey*/) { - return m_nullThumbnail; + return false; } - bool ThumbnailerNullComponent::IsLoading(AzToolsFramework::Thumbnailer::SharedThumbnailKey /*thumbnailKey*/, const char* /*contextName*/) + QThreadPool* ThumbnailerNullComponent::GetThreadPool() { - return false; + return nullptr; } } // namespace Thumbnailer } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerNullComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerNullComponent.h index 41ae051b0d..5fec43ee7c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerNullComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Thumbnails/ThumbnailerNullComponent.h @@ -19,11 +19,9 @@ namespace AzToolsFramework { namespace Thumbnailer { - class ThumbnailContext; - class ThumbnailerNullComponent : public AZ::Component - , public AzToolsFramework::Thumbnailer::ThumbnailerRequestsBus::Handler + , public AzToolsFramework::Thumbnailer::ThumbnailerRequestBus::Handler { public: AZ_COMPONENT(ThumbnailerNullComponent, "{8009D651-3FAA-9815-B99E-AF174A3B29D4}") @@ -42,13 +40,12 @@ namespace AzToolsFramework ////////////////////////////////////////////////////////////////////////// // ThumbnailerRequests ////////////////////////////////////////////////////////////////////////// - void RegisterContext(const char* contextName) override; - void UnregisterContext(const char* contextName) override; - bool HasContext(const char* contextName) const override; - void RegisterThumbnailProvider(AzToolsFramework::Thumbnailer::SharedThumbnailProvider provider, const char* contextName) override; - void UnregisterThumbnailProvider(const char* providerName, const char* contextName) override; - AzToolsFramework::Thumbnailer::SharedThumbnail GetThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, const char* contextName) override; - bool IsLoading(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, const char* contextName) override; + void RegisterThumbnailProvider(AzToolsFramework::Thumbnailer::SharedThumbnailProvider provider) override; + void UnregisterThumbnailProvider(const char* providerName) override; + AzToolsFramework::Thumbnailer::SharedThumbnail GetThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey) override; + bool IsLoading(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey) override; + QThreadPool* GetThreadPool() override; + private: AzToolsFramework::Thumbnailer::SharedThumbnail m_nullThumbnail; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp index 014bade347..1697259dc9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/TransformComponent.cpp @@ -1202,6 +1202,7 @@ namespace AzToolsFramework Attribute(AZ::Edit::Attributes::FixedComponentListIndex, 0)-> Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Transform.svg")-> Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Transform.svg")-> + Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/transform/")-> Attribute(AZ::Edit::Attributes::AutoExpand, true)-> DataElement(AZ::Edit::UIHandlers::Default, &TransformComponent::m_parentEntityId, "Parent entity", "")-> Attribute(AZ::Edit::Attributes::ChangeValidate, &TransformComponent::ValidatePotentialParent)-> diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp index 45363f4bb5..ac7d832c66 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp @@ -2091,8 +2091,14 @@ namespace AzToolsFramework customOption.state ^= QStyle::State_HasFocus; } + // Don't allow to paint on the spacing column + if (index.column() == EntityOutlinerListModel::ColumnSpacing) + { + return; + } + // Retrieve the Entity UI Handler - auto firstColumnIndex = index.siblingAtColumn(0); + auto firstColumnIndex = index.siblingAtColumn(EntityOutlinerListModel::ColumnName); AZ::EntityId entityId(firstColumnIndex.data(EntityOutlinerListModel::EntityIdRole).value()); auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx index 51d047e81a..5c93d11c37 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx @@ -69,6 +69,7 @@ namespace AzToolsFramework ColumnName, //!< Entity name ColumnVisibilityToggle, //!< Visibility Icons ColumnLockToggle, //!< Lock Icons + ColumnSpacing, //!< Spacing to allow for drag select ColumnSortIndex, //!< Index of sort order ColumnCount //!< Total number of columns }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp index c2bbebb72e..8f0d541017 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp @@ -30,7 +30,6 @@ namespace AzToolsFramework EntityOutlinerTreeView::EntityOutlinerTreeView(QWidget* pParent) : AzQtComponents::StyledTreeView(pParent) , m_queuedMouseEvent(nullptr) - , m_draggingUnselectedItem(false) { setUniformRowHeights(true); setHeaderHidden(true); @@ -65,22 +64,6 @@ namespace AzToolsFramework m_expandOnlyDelay = delay; } - void EntityOutlinerTreeView::ClearQueuedMouseEvent() - { - if (m_queuedMouseEvent) - { - delete m_queuedMouseEvent; - m_queuedMouseEvent = nullptr; - } - } - - void EntityOutlinerTreeView::leaveEvent([[maybe_unused]] QEvent* event) - { - m_mousePosition = QPoint(-1, -1); - m_currentHoveredIndex = QModelIndex(); - update(); - } - void EntityOutlinerTreeView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles) { AzQtComponents::StyledTreeView::dataChanged(topLeft, bottomRight, roles); @@ -93,7 +76,7 @@ namespace AzToolsFramework auto modelRow = topLeft.sibling(i, EntityOutlinerListModel::ColumnName); if (modelRow.isValid()) { - checkExpandedState(modelRow); + CheckExpandedState(modelRow); } } } @@ -108,15 +91,15 @@ namespace AzToolsFramework auto modelRow = model()->index(i, EntityOutlinerListModel::ColumnName, parent); if (modelRow.isValid()) { - checkExpandedState(modelRow); - recursiveCheckExpandedStates(modelRow); + CheckExpandedState(modelRow); + RecursiveCheckExpandedStates(modelRow); } } } AzQtComponents::StyledTreeView::rowsInserted(parent, start, end); } - void EntityOutlinerTreeView::recursiveCheckExpandedStates(const QModelIndex& current) + void EntityOutlinerTreeView::RecursiveCheckExpandedStates(const QModelIndex& current) { const int rowCount = model()->rowCount(current); for (int i = 0; i < rowCount; i++) @@ -124,13 +107,13 @@ namespace AzToolsFramework auto modelRow = model()->index(i, EntityOutlinerListModel::ColumnName, current); if (modelRow.isValid()) { - checkExpandedState(modelRow); - recursiveCheckExpandedStates(modelRow); + CheckExpandedState(modelRow); + RecursiveCheckExpandedStates(modelRow); } } } - void EntityOutlinerTreeView::checkExpandedState(const QModelIndex& current) + void EntityOutlinerTreeView::CheckExpandedState(const QModelIndex& current) { const bool expandState = current.data(EntityOutlinerListModel::ExpandedRole).template value(); setExpanded(current, expandState); @@ -138,87 +121,125 @@ namespace AzToolsFramework void EntityOutlinerTreeView::mousePressEvent(QMouseEvent* event) { - //postponing normal mouse pressed logic until mouse is released or dragged - //this means selection occurs on mouse released now - //this is to support drag/drop of non-selected items + // Postponing normal mouse press logic until mouse is released or dragged. + // This allows drag/drop of non-selected items. ClearQueuedMouseEvent(); m_queuedMouseEvent = new QMouseEvent(*event); } - void EntityOutlinerTreeView::mouseReleaseEvent(QMouseEvent* event) + void EntityOutlinerTreeView::mouseMoveEvent(QMouseEvent* event) { - if (m_queuedMouseEvent && !m_draggingUnselectedItem) + // Prevent multiple updates throughout the function for changing UIs. + bool forceUpdate = false; + + QModelIndex previousHoveredIndex = m_currentHoveredIndex; + m_mousePosition = event->pos(); + + if (QModelIndex hoveredIndex = indexAt(m_mousePosition); + m_currentHoveredIndex != hoveredIndex) + { + m_currentHoveredIndex = hoveredIndex; + } + + if (m_queuedMouseEvent) + { + if (!m_isDragSelectActive) + { + // Determine whether the mouse move should trigger a rect selection or an entity drag. + QModelIndex clickedIndex = indexAt(m_queuedMouseEvent->pos()); + // Even though the drag started on an index, we want to trigger a drag select from the last column. + // This is to allow drag selection to be triggered from anywhere in the hierarchy. + if (clickedIndex.isValid() && clickedIndex.column() != EntityOutlinerListModel::ColumnSpacing) + { + HandleDrag(); + } + else + { + m_isDragSelectActive = true; + forceUpdate = true; + } + } + else + { + SelectAllEntitiesInSelectionRect(); + forceUpdate = true; + } + } + + if (previousHoveredIndex != m_currentHoveredIndex) { - // mouseMoveEvent will set the state to be DraggingState, which will make Qt ignore - // mousePressEvent in QTreeViewPrivate::expandOrCollapseItemAtPos. So we manually - // and temporarily set it to EditingState. - QAbstractItemView::State stateBefore = QAbstractItemView::state(); - QAbstractItemView::setState(QAbstractItemView::State::EditingState); + forceUpdate = true; + } - //treat this as a mouse pressed event to process selection etc - processQueuedMousePressedEvent(m_queuedMouseEvent); + if (forceUpdate) + { + update(); + } + } - QAbstractItemView::setState(stateBefore); + void EntityOutlinerTreeView::mouseReleaseEvent(QMouseEvent* event) + { + if (m_isDragSelectActive) + { + SelectAllEntitiesInSelectionRect(); + update(); + } + else if (m_queuedMouseEvent) + { + ProcessQueuedMousePressedEvent(m_queuedMouseEvent); } ClearQueuedMouseEvent(); - m_draggingUnselectedItem = false; + m_isDragSelectActive = false; QTreeView::mouseReleaseEvent(event); } void EntityOutlinerTreeView::mouseDoubleClickEvent(QMouseEvent* event) { - //cancel pending mouse press + // Cancel pending mouse press. ClearQueuedMouseEvent(); QTreeView::mouseDoubleClickEvent(event); } - void EntityOutlinerTreeView::mouseMoveEvent(QMouseEvent* event) + void EntityOutlinerTreeView::focusInEvent(QFocusEvent* event) { - if (m_queuedMouseEvent) - { - //disable selection for the pending click if the mouse moved so selection is maintained for dragging - QAbstractItemView::SelectionMode selectionModeBefore = selectionMode(); - setSelectionMode(QAbstractItemView::NoSelection); - - //treat this as a mouse pressed event to process everything but selection, but use the position data from the mousePress message - processQueuedMousePressedEvent(m_queuedMouseEvent); + // Cancel pending mouse press. + ClearQueuedMouseEvent(); + QTreeView::focusInEvent(event); + } - //restore selection state - setSelectionMode(selectionModeBefore); - } + void EntityOutlinerTreeView::focusOutEvent(QFocusEvent* event) + { + // Cancel pending mouse press. + ClearQueuedMouseEvent(); + QTreeView::focusOutEvent(event); + } - m_mousePosition = event->pos(); - if (QModelIndex hoveredIndex = indexAt(m_mousePosition); m_currentHoveredIndex != indexAt(m_mousePosition)) + void EntityOutlinerTreeView::dragMoveEvent([[maybe_unused]] QDragMoveEvent* event) + { + if (m_expandOnlyDelay >= 0) { - m_currentHoveredIndex = hoveredIndex; - update(); + m_expandTimer.start(m_expandOnlyDelay, this); } - //process mouse movement as normal, potentially triggering drag and drop - QTreeView::mouseMoveEvent(event); + QTreeView::dragMoveEvent(event); } - void EntityOutlinerTreeView::focusInEvent(QFocusEvent* event) + void EntityOutlinerTreeView::dropEvent([[maybe_unused]] QDropEvent* event) { - //cancel pending mouse press - ClearQueuedMouseEvent(); - QTreeView::focusInEvent(event); - } + emit ItemDropped(); + QTreeView::dropEvent(event); - void EntityOutlinerTreeView::focusOutEvent(QFocusEvent* event) - { - //cancel pending mouse press ClearQueuedMouseEvent(); - QTreeView::focusOutEvent(event); } - void EntityOutlinerTreeView::startDrag(Qt::DropActions supportedActions) + void EntityOutlinerTreeView::HandleDrag() { - QModelIndex index = indexAt(m_queuedMouseEvent->pos()); - AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value()); + // Retrieve the index at the click position. + QModelIndex indexAtClick = indexAt(m_queuedMouseEvent->pos()).siblingAtColumn(EntityOutlinerListModel::ColumnName); + AZ::EntityId entityId(indexAtClick.data(EntityOutlinerListModel::EntityIdRole).value()); AZ::EntityId parentEntityId; EditorEntityInfoRequestBus::EventResult(parentEntityId, entityId, &EditorEntityInfoRequestBus::Events::GetParent); @@ -228,41 +249,116 @@ namespace AzToolsFramework return; } - //if we are attempting to drag an unselected item then we must special case drag and drop logic - //QAbstractItemView::startDrag only supports selected items - if (m_queuedMouseEvent) + // If the index is selected, we should move the whole selection. + if (selectionModel()->isSelected(indexAtClick)) { - if (!index.isValid() || index.column() != 0) - { - return; - } + StartCustomDrag(selectionModel()->selectedIndexes(), defaultDropAction()); + } + else + { + StartCustomDrag(QModelIndexList{ indexAtClick }, defaultDropAction()); + } + } - if (!selectionModel()->isSelected(index)) - { - StartCustomDrag({ index }, supportedActions); - return; - } + void EntityOutlinerTreeView::SelectAllEntitiesInSelectionRect() + { + if (!m_queuedMouseEvent) + { + return; + } + + // Retrieve the two opposing corners of the rect. + const QPoint point1 = (m_queuedMouseEvent->pos()); // The position the drag operation started at. + const QPoint point2 = (m_mousePosition); // The current mouse position. + + // Determine which point's y is the top and which is the bottom. + const int top(AZStd::min(point1.y(), point2.y())); + const int bottom(AZStd::max(point1.y(), point2.y())); + // We don't really need the x values for the rect, just use the center of the viewport. + const int middle(viewport()->rect().center().x()); + + // Find the extremes of the range of indices that are in the selection rect. + QModelIndex topIndex = indexAt(QPoint(middle, top)); + const QModelIndex bottomIndex = indexAt(QPoint(middle, bottom)); + + // If we have no top index, the mouse may have been dragged above the top item. Let's try to course correct. + const int topDistanceForFirstItem = 10; // A reasonable distance from the top we're sure to encounter the first item. + const QModelIndex firstIndex = indexAt(QPoint(middle, topDistanceForFirstItem)); + + if (!topIndex.isValid() && top < topDistanceForFirstItem) + { + topIndex = firstIndex; } - StyledTreeView::startDrag(supportedActions); + // We can assume that if topIndex is still invalid, it was below the last item in the hierarchy, hence no selection is made. + if (!topIndex.isValid()) + { + return; + } + + QItemSelection selection; + + // Starting from the top index, traverse all visible elements of the list and select them until the bottom index is hit. + // If the bottom index is undefined, just keep going to the end. + QModelIndex iter = topIndex; + selection.select(iter, iter); + + while (iter.isValid() && iter != bottomIndex) + { + iter = indexBelow(iter); + selection.select(iter, iter); + } + + selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } - void EntityOutlinerTreeView::dragMoveEvent(QDragMoveEvent* event) + void EntityOutlinerTreeView::ClearQueuedMouseEvent() { - if (m_expandOnlyDelay >= 0) + if (m_queuedMouseEvent) { - m_expandTimer.start(m_expandOnlyDelay, this); + delete m_queuedMouseEvent; + m_queuedMouseEvent = nullptr; } + } - QTreeView::dragMoveEvent(event); + void EntityOutlinerTreeView::leaveEvent([[maybe_unused]] QEvent* event) + { + ClearQueuedMouseEvent(); + + // Only clear the mouse position if the last mouse position registered is inside. + // This allows drag to select to work correctly in all situations. + if(this->viewport()->rect().contains(m_mousePosition)) + { + m_mousePosition = QPoint(-1, -1); + } + m_currentHoveredIndex = QModelIndex(); + update(); } - void EntityOutlinerTreeView::dropEvent(QDropEvent* event) + void EntityOutlinerTreeView::paintEvent(QPaintEvent* event) { - emit ItemDropped(); - QTreeView::dropEvent(event); + AzQtComponents::StyledTreeView::paintEvent(event); - m_draggingUnselectedItem = false; + // Draw the drag selection rect. + if (m_isDragSelectActive && m_queuedMouseEvent) + { + // Create a painter to draw on the viewport. + QPainter painter(viewport()); + + // Retrieve the two corners of the rect. + const QPoint point1 = (m_queuedMouseEvent->pos()); // The position the drag operation started at. + const QPoint point2 = (m_mousePosition); // The current mouse position. + + // We need the top left and bottom right corners, which may not be the two corners we got above. + // So we composite the corners based on the coordinates of the points. + const QPoint topLeft(AZStd::min(point1.x(), point2.x()), AZStd::min(point1.y(), point2.y())); + const QPoint bottomRight(AZStd::max(point1.x(), point2.x()), AZStd::max(point1.y(), point2.y())); + + // Paint the rect. + painter.setBrush(m_dragSelectRectColor); + painter.setPen(m_dragSelectBorderColor); + painter.drawRect(QRect(topLeft, bottomRight)); + } } void EntityOutlinerTreeView::drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const @@ -270,7 +366,7 @@ namespace AzToolsFramework const bool isEnabled = (this->model()->flags(index) & Qt::ItemIsEnabled); const bool isSelected = selectionModel()->isSelected(index); - const bool isHovered = (index == indexAt(m_mousePosition).siblingAtColumn(0)) && isEnabled; + const bool isHovered = (index == m_currentHoveredIndex.siblingAtColumn(0)) && isEnabled; // Paint the branch Selection/Hover Rect PaintBranchSelectionHoverRect(painter, rect, isSelected, isHovered); @@ -352,25 +448,27 @@ namespace AzToolsFramework QTreeView::timerEvent(event); } - void EntityOutlinerTreeView::processQueuedMousePressedEvent(QMouseEvent* event) + void EntityOutlinerTreeView::ProcessQueuedMousePressedEvent(QMouseEvent* event) { - //interpret the mouse event as a button press - QMouseEvent mousePressedEvent( - QEvent::MouseButtonPress, - event->localPos(), - event->windowPos(), - event->screenPos(), - event->button(), - event->buttons(), - event->modifiers(), - event->source()); - QTreeView::mousePressEvent(&mousePressedEvent); + QModelIndex clickedIndex = indexAt(m_queuedMouseEvent->pos()); + if (!clickedIndex.isValid() || clickedIndex.column() != EntityOutlinerListModel::ColumnSpacing) + { + //interpret the mouse event as a button press + QMouseEvent mousePressedEvent( + QEvent::MouseButtonPress, + event->localPos(), + event->windowPos(), + event->screenPos(), + event->button(), + event->buttons(), + event->modifiers(), + event->source()); + QTreeView::mousePressEvent(&mousePressedEvent); + } } void EntityOutlinerTreeView::StartCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions) { - m_draggingUnselectedItem = true; - //sort by container entity depth and order in hierarchy for proper drag image and drop order QModelIndexList indexListSorted = indexList; AZStd::unordered_map> locations; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx index 014bb7bd48..6b4fa656e5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx @@ -63,7 +63,6 @@ namespace AzToolsFramework void mouseMoveEvent(QMouseEvent* event) override; void focusInEvent(QFocusEvent* event) override; void focusOutEvent(QFocusEvent* event) override; - void startDrag(Qt::DropActions supportedActions) override; void dragMoveEvent(QDragMoveEvent* event) override; void dropEvent(QDropEvent* event) override; void leaveEvent(QEvent* event) override; @@ -71,33 +70,39 @@ namespace AzToolsFramework // FocusModeNotificationBus overrides ... void OnEditorFocusChanged(AZ::EntityId previousFocusEntityId, AZ::EntityId newFocusEntityId) override; + void paintEvent(QPaintEvent* event) override; + //! Renders the left side of the item: appropriate background, branch lines, icons. void drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const override; void timerEvent(QTimerEvent* event) override; private: void ClearQueuedMouseEvent(); + void ProcessQueuedMousePressedEvent(QMouseEvent* event); - void processQueuedMousePressedEvent(QMouseEvent* event); - void recursiveCheckExpandedStates(const QModelIndex& parent); - void checkExpandedState(const QModelIndex& current); + void SelectAllEntitiesInSelectionRect(); + void HandleDrag(); void StartCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions) override; + void RecursiveCheckExpandedStates(const QModelIndex& parent); + void CheckExpandedState(const QModelIndex& current); + void PaintBranchBackground(QPainter* painter, const QRect& rect, const QModelIndex& index) const; void PaintBranchSelectionHoverRect(QPainter* painter, const QRect& rect, bool isSelected, bool isHovered) const; QMouseEvent* m_queuedMouseEvent; + QModelIndex m_currentHoveredIndex; QPoint m_mousePosition; - bool m_draggingUnselectedItem; // This is set when an item is dragged outside its bounding box. + bool m_isDragSelectActive = false; int m_expandOnlyDelay = -1; QBasicTimer m_expandTimer; const QColor m_selectedColor = QColor(255, 255, 255, 45); const QColor m_hoverColor = QColor(255, 255, 255, 30); - - QModelIndex m_currentHoveredIndex; + const QColor m_dragSelectRectColor = QColor(255, 255, 255, 20); + const QColor m_dragSelectBorderColor = QColor(255, 255, 255); EditorEntityUiInterface* m_editorEntityFrameworkInterface = nullptr; ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp index f4855cb715..604c3660e6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerWidget.cpp @@ -193,7 +193,6 @@ namespace AzToolsFramework m_listModel->SetSortMode(m_sortMode); const int autoExpandDelayMilliseconds = 2500; - m_gui->m_objectTree->setSelectionMode(QAbstractItemView::ExtendedSelection); SetDefaultTreeViewEditTriggers(); m_gui->m_objectTree->setAutoExpandDelay(autoExpandDelayMilliseconds); m_gui->m_objectTree->setDragEnabled(true); @@ -208,6 +207,7 @@ namespace AzToolsFramework m_gui->m_objectTree->setAutoScrollMargin(20); m_gui->m_objectTree->setIndentation(24); m_gui->m_objectTree->setRootIsDecorated(false); + m_gui->m_objectTree->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(m_gui->m_objectTree, &QTreeView::customContextMenuRequested, this, &EntityOutlinerWidget::OnOpenTreeContextMenu); // custom item delegate @@ -260,6 +260,8 @@ namespace AzToolsFramework m_gui->m_objectTree->header()->resizeSection(EntityOutlinerListModel::ColumnVisibilityToggle, 20); m_gui->m_objectTree->header()->setSectionResizeMode(EntityOutlinerListModel::ColumnLockToggle, QHeaderView::Fixed); m_gui->m_objectTree->header()->resizeSection(EntityOutlinerListModel::ColumnLockToggle, 24); + m_gui->m_objectTree->header()->setSectionResizeMode(EntityOutlinerListModel::ColumnSpacing, QHeaderView::Fixed); + m_gui->m_objectTree->header()->resizeSection(EntityOutlinerListModel::ColumnSpacing, 16); connect(m_gui->m_objectTree->selectionModel(), &QItemSelectionModel::selectionChanged, diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index dfa8ad14c9..6e8bad0217 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -8,21 +8,14 @@ #include +#include +#include #include #include -#include -#include -#include -#include #include #include -#include -#include -#include -#include -#include #include #include #include @@ -32,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -39,27 +33,11 @@ #include #include -#include -#include -#include -#include - #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include -#include #include -#include -#include namespace AzToolsFramework { @@ -72,31 +50,6 @@ namespace AzToolsFramework PrefabFocusPublicInterface* PrefabIntegrationManager::s_prefabFocusPublicInterface = nullptr; PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr; PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr; - PrefabSystemComponentInterface* PrefabIntegrationManager::s_prefabSystemComponentInterface = nullptr; - - const AZStd::string PrefabIntegrationManager::s_prefabFileExtension = ".prefab"; - - static const char* const ClosePrefabDialog = "ClosePrefabDialog"; - static const char* const FooterSeparatorLine = "FooterSeparatorLine"; - static const char* const PrefabSavedMessageFrame = "PrefabSavedMessageFrame"; - static const char* const PrefabSavePreferenceHint = "PrefabSavePreferenceHint"; - static const char* const PrefabSaveWarningFrame = "PrefabSaveWarningFrame"; - static const char* const SaveDependentPrefabsCard = "SaveDependentPrefabsCard"; - static const char* const SavePrefabDialog = "SavePrefabDialog"; - static const char* const UnsavedPrefabFileName = "UnsavedPrefabFileName"; - - - void PrefabUserSettings::Reflect(AZ::ReflectContext* context) - { - AZ::SerializeContext* serializeContext = azrtti_cast(context); - if (serializeContext) - { - serializeContext->Class() - ->Version(1) - ->Field("m_saveLocation", &PrefabUserSettings::m_saveLocation) - ->Field("m_autoNumber", &PrefabUserSettings::m_autoNumber); - } - } PrefabIntegrationManager::PrefabIntegrationManager() { @@ -128,13 +81,6 @@ namespace AzToolsFramework return; } - s_prefabSystemComponentInterface = AZ::Interface::Get(); - if (s_prefabSystemComponentInterface == nullptr) - { - AZ_Assert(false, "Prefab - could not get PrefabSystemComponentInterface on PrefabIntegrationManager construction."); - return; - } - s_prefabFocusPublicInterface = AZ::Interface::Get(); if (s_prefabFocusPublicInterface == nullptr) { @@ -143,7 +89,9 @@ namespace AzToolsFramework } m_readOnlyEntityPublicInterface = AZ::Interface::Get(); - AZ_Assert(m_readOnlyEntityPublicInterface, "Prefab - could not get ReadOnlyEntityPublicInterface on PrefabIntegrationManager construction."); + AZ_Assert( + m_readOnlyEntityPublicInterface, + "Prefab - could not get ReadOnlyEntityPublicInterface on PrefabIntegrationManager construction."); // Get EditorEntityContextId EditorEntityContextRequestBus::BroadcastResult(s_editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); @@ -156,7 +104,6 @@ namespace AzToolsFramework EditorEventsBus::Handler::BusConnect(); PrefabInstanceContainerNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); - AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension); EditorEntityContextNotificationBus::Handler::BusConnect(); InitializeShortcuts(); @@ -167,7 +114,6 @@ namespace AzToolsFramework UninitializeShortcuts(); EditorEntityContextNotificationBus::Handler::BusDisconnect(); - AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect(); AZ::Interface::Unregister(this); PrefabInstanceContainerNotificationBus::Handler::BusDisconnect(); EditorEventsBus::Handler::BusDisconnect(); @@ -257,7 +203,8 @@ namespace AzToolsFramework return "Prefabs"; } - void PrefabIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags) + void PrefabIntegrationManager::PopulateEditorGlobalContextMenu( + QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags) { AzToolsFramework::EntityIdList selectedEntities; AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult( @@ -330,9 +277,12 @@ namespace AzToolsFramework QAction* createAction = menu->addAction(QObject::tr("Create Prefab...")); createAction->setToolTip(QObject::tr("Creates a prefab out of the currently selected entities.")); - QObject::connect(createAction, &QAction::triggered, createAction, [selectedEntities] { - ContextMenu_CreatePrefab(selectedEntities); - }); + QObject::connect( + createAction, &QAction::triggered, createAction, + [selectedEntities] + { + ContextMenu_CreatePrefab(selectedEntities); + }); itemWasShown = true; } @@ -348,7 +298,11 @@ namespace AzToolsFramework instantiateAction->setToolTip(QObject::tr("Instantiates a prefab file in the scene.")); QObject::connect( - instantiateAction, &QAction::triggered, instantiateAction, [] { ContextMenu_InstantiatePrefab(); }); + instantiateAction, &QAction::triggered, instantiateAction, + [] + { + ContextMenu_InstantiatePrefab(); + }); // Instantiate Procedural Prefab if (AZ::Prefab::ProceduralPrefabAsset::UseProceduralPrefabs()) @@ -357,7 +311,11 @@ namespace AzToolsFramework action->setToolTip(QObject::tr("Instantiates a procedural prefab file in a prefab.")); QObject::connect( - action, &QAction::triggered, action, [] { ContextMenu_InstantiateProceduralPrefab(); }); + action, &QAction::triggered, action, + [] + { + ContextMenu_InstantiateProceduralPrefab(); + }); } itemWasShown = true; @@ -433,9 +391,12 @@ namespace AzToolsFramework QAction* saveAction = menu->addAction(QObject::tr("Save Prefab to file")); saveAction->setToolTip(QObject::tr("Save the changes to the prefab to disk.")); - QObject::connect(saveAction, &QAction::triggered, saveAction, [selectedEntity] { - ContextMenu_SavePrefab(selectedEntity); - }); + QObject::connect( + saveAction, &QAction::triggered, saveAction, + [selectedEntity] + { + ContextMenu_SavePrefab(selectedEntity); + }); } itemWasShown = true; @@ -454,7 +415,12 @@ namespace AzToolsFramework !readOnlyEntityInSelection) { QAction* deleteAction = menu->addAction(QObject::tr("Delete")); - QObject::connect(deleteAction, &QAction::triggered, deleteAction, [] { ContextMenu_DeleteSelected(); }); + QObject::connect( + deleteAction, &QAction::triggered, deleteAction, + [] + { + ContextMenu_DeleteSelected(); + }); } // Detach Prefab @@ -477,16 +443,6 @@ namespace AzToolsFramework s_prefabFocusPublicInterface->FocusOnOwningPrefab(AZ::EntityId()); } - void PrefabIntegrationManager::HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const - { - auto instantiatePrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(sourceFilePath, parentId, position); - - if (!instantiatePrefabOutcome.IsSuccess()) - { - WarnUserOfError("Prefab Instantiation Error", instantiatePrefabOutcome.GetError()); - } - } - void PrefabIntegrationManager::OnStartPlayInEditorBegin() { // Focus on the root prefab (AZ::EntityId() will default to it) @@ -501,8 +457,7 @@ namespace AzToolsFramework [&]() { s_containerEntityInterface->RefreshAllContainerEntities(s_editorEntityContextId); - } - ); + }); } void PrefabIntegrationManager::ContextMenu_CreatePrefab(AzToolsFramework::EntityIdList selectedEntities) @@ -553,7 +508,8 @@ namespace AzToolsFramework if (hasExternalReferences) { bool useAllReferencedEntities = false; - bool continueCreation = QueryAndPruneMissingExternalReferences(entitiesToIncludeInAsset, allReferencedEntities, useAllReferencedEntities); + bool continueCreation = + QueryAndPruneMissingExternalReferences(entitiesToIncludeInAsset, allReferencedEntities, useAllReferencedEntities); if (!continueCreation) { // User canceled the operation @@ -576,17 +532,19 @@ namespace AzToolsFramework { AZ::EntityId commonRoot; bool hasCommonRoot = false; - AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(hasCommonRoot, - &AzToolsFramework::ToolsApplicationRequests::FindCommonRoot, entitiesToIncludeInAsset, commonRoot, &prefabRootEntities); - if (hasCommonRoot && commonRoot.IsValid() && entitiesToIncludeInAsset.find(commonRoot) != entitiesToIncludeInAsset.end()) + AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult( + hasCommonRoot, &AzToolsFramework::ToolsApplicationRequests::FindCommonRoot, entitiesToIncludeInAsset, commonRoot, + &prefabRootEntities); + if (hasCommonRoot && commonRoot.IsValid() && + entitiesToIncludeInAsset.find(commonRoot) != entitiesToIncludeInAsset.end()) { prefabRootEntities.insert(prefabRootEntities.begin(), commonRoot); } } - GenerateSuggestedFilenameFromEntities(prefabRootEntities, suggestedName); + PrefabSaveHandler::GenerateSuggestedFilenameFromEntities(prefabRootEntities, suggestedName); - if (!QueryUserForPrefabSaveLocation( + if (!PrefabSaveHandler::QueryUserForPrefabSaveLocation( suggestedName, targetDirectory, AZ_CRC("PrefabUserSettings"), activeWindow, prefabName, prefabFilePath)) { // User canceled prefab creation, or error prevented continuation. @@ -598,14 +556,14 @@ namespace AzToolsFramework if (!createPrefabOutcome.IsSuccess()) { - WarnUserOfError("Prefab Creation Error", createPrefabOutcome.GetError()); + WarningDialog("Prefab Creation Error", createPrefabOutcome.GetError()); } } void PrefabIntegrationManager::ContextMenu_InstantiatePrefab() { AZStd::string prefabFilePath; - bool hasUserSelectedValidSourceFile = QueryUserForPrefabFilePath(prefabFilePath); + bool hasUserSelectedValidSourceFile = PrefabSaveHandler::QueryUserForPrefabFilePath(prefabFilePath); if (hasUserSelectedValidSourceFile) { @@ -629,7 +587,7 @@ namespace AzToolsFramework auto createPrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(prefabFilePath, parentId, position); if (!createPrefabOutcome.IsSuccess()) { - WarnUserOfError("Prefab Instantiation Error",createPrefabOutcome.GetError()); + WarningDialog("Prefab Instantiation Error", createPrefabOutcome.GetError()); } } } @@ -637,7 +595,7 @@ namespace AzToolsFramework void PrefabIntegrationManager::ContextMenu_InstantiateProceduralPrefab() { AZStd::string prefabAssetPath; - bool hasUserForProceduralPrefabAsset = QueryUserForProceduralPrefabAsset(prefabAssetPath); + bool hasUserForProceduralPrefabAsset = PrefabSaveHandler::QueryUserForProceduralPrefabAsset(prefabAssetPath); if (hasUserForProceduralPrefabAsset) { @@ -659,7 +617,7 @@ namespace AzToolsFramework auto createPrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(prefabAssetPath, parentId, position); if (!createPrefabOutcome.IsSuccess()) { - WarnUserOfError("Procedural Prefab Instantiation Error", createPrefabOutcome.GetError()); + WarningDialog("Procedural Prefab Instantiation Error", createPrefabOutcome.GetError()); } } } @@ -682,7 +640,7 @@ namespace AzToolsFramework if (!savePrefabOutcome.IsSuccess()) { - WarnUserOfError("Prefab Save Error", savePrefabOutcome.GetError()); + WarningDialog("Prefab Save Error", savePrefabOutcome.GetError()); } } @@ -695,401 +653,22 @@ namespace AzToolsFramework s_prefabPublicInterface->DeleteEntitiesAndAllDescendantsInInstance(selectedEntityIds); if (!deleteSelectedResult.IsSuccess()) { - WarnUserOfError("Delete selected entities error", deleteSelectedResult.GetError()); + WarningDialog("Delete selected entities error", deleteSelectedResult.GetError()); } } void PrefabIntegrationManager::ContextMenu_DetachPrefab(AZ::EntityId containerEntity) { - PrefabOperationResult detachPrefabResult = - s_prefabPublicInterface->DetachPrefab(containerEntity); + PrefabOperationResult detachPrefabResult = s_prefabPublicInterface->DetachPrefab(containerEntity); if (!detachPrefabResult.IsSuccess()) { - WarnUserOfError("Detach Prefab error", detachPrefabResult.GetError()); - } - } - - void PrefabIntegrationManager::GenerateSuggestedFilenameFromEntities(const EntityIdList& entityIds, AZStd::string& outName) - { - AZ_PROFILE_FUNCTION(AzToolsFramework); - - AZStd::string suggestedName; - - for (const AZ::EntityId& entityId : entityIds) - { - if (!AppendEntityToSuggestedFilename(suggestedName, entityId)) - { - break; - } - } - - if (suggestedName.size() == 0 || AzFramework::StringFunc::Utf8::CheckNonAsciiChar(suggestedName)) - { - suggestedName = "NewPrefab"; - } - - outName = suggestedName; - } - - bool PrefabIntegrationManager::AppendEntityToSuggestedFilename(AZStd::string& filename, AZ::EntityId entityId) - { - // When naming a prefab after its entities, we stop appending additional names once we've reached this cutoff length - size_t prefabNameCutoffLength = 32; - AzToolsFramework::EntityIdSet usedNameEntities; - - if (usedNameEntities.find(entityId) == usedNameEntities.end()) - { - AZ::Entity* entity = nullptr; - AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, entityId); - if (entity) - { - AZStd::string entityNameFiltered = entity->GetName(); - - // Convert spaces in entity names to underscores - for (size_t i = 0; i < entityNameFiltered.size(); ++i) - { - char& character = entityNameFiltered.at(i); - if (character == ' ') - { - character = '_'; - } - } - - filename.append(entityNameFiltered); - usedNameEntities.insert(entityId); - if (filename.size() > prefabNameCutoffLength) - { - return false; - } - } - } - - return true; - } - - bool PrefabIntegrationManager::QueryUserForPrefabSaveLocation( - const AZStd::string& suggestedName, - const char* initialTargetDirectory, - AZ::u32 prefabUserSettingsId, - QWidget* activeWindow, - AZStd::string& outPrefabName, - AZStd::string& outPrefabFilePath - ) - { - AZStd::string saveAsInitialSuggestedDirectory; - if (!GetPrefabSaveLocation(saveAsInitialSuggestedDirectory, prefabUserSettingsId)) - { - saveAsInitialSuggestedDirectory = initialTargetDirectory; - } - - AZStd::string saveAsInitialSuggestedFullPath; - GenerateSuggestedPrefabPath(suggestedName, saveAsInitialSuggestedDirectory, saveAsInitialSuggestedFullPath); - - QString saveAs; - AZStd::string targetPath; - QFileInfo prefabSaveFileInfo; - QString prefabName; - while (true) - { - { - AZ_PROFILE_FUNCTION(AzToolsFramework); - saveAs = QFileDialog::getSaveFileName(nullptr, QString("Save As..."), saveAsInitialSuggestedFullPath.c_str(), QString("Prefabs (*.prefab)")); - } - - prefabSaveFileInfo = saveAs; - prefabName = prefabSaveFileInfo.baseName(); - if (saveAs.isEmpty()) - { - return false; - } - - targetPath = saveAs.toUtf8().constData(); - if (AzFramework::StringFunc::Utf8::CheckNonAsciiChar(targetPath)) - { - WarnUserOfError( - "Prefab Creation Failed.", - "Unicode file name is not supported. \r\n" - "Please use ASCII characters to name your prefab." - ); - return false; - } - - PrefabSaveResult saveResult = IsPrefabPathValidForAssets(activeWindow, saveAs, saveAsInitialSuggestedFullPath); - if (saveResult == PrefabSaveResult::Cancel) - { - // The error was already reported if this failed. - return false; - } - else if (saveResult == PrefabSaveResult::Continue) - { - // The prefab save name is valid, continue with the save attempt. - break; - } + WarningDialog("Detach Prefab error", detachPrefabResult.GetError()); } - - // If the prefab already exists, notify the user and bail - AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); - if (fileIO && fileIO->Exists(targetPath.c_str())) - { - const AZStd::string message = AZStd::string::format( - "You are attempting to overwrite an existing prefab: \"%s\".\r\n\r\n" - "This will damage instances or cascades of this prefab. \r\n\r\n" - "Instead, either push entities/fields to the prefab, or save to a different location.", - targetPath.c_str()); - - WarnUserOfError("Prefab Already Exists", message); - return false; - } - - // We prevent users from creating a new prefab with the same relative path that's already - // been used by an existing prefab in other places (e.g. Gems) because the AssetProcessor - // generates asset ids based on relative paths. This is unnecessary once AssetProcessor - // starts to generate UUID to every asset regardless of paths. - { - AZStd::string prefabRelativeName; - bool relativePathFound; - AssetSystemRequestBus::BroadcastResult(relativePathFound, &AssetSystemRequestBus::Events::GetRelativeProductPathFromFullSourceOrProductPath, targetPath, prefabRelativeName); - - AZ::Data::AssetId prefabAssetId; - AZ::Data::AssetCatalogRequestBus::BroadcastResult(prefabAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, prefabRelativeName.c_str(), AZ::Data::s_invalidAssetType, false); - if (prefabAssetId.IsValid()) - { - const AZStd::string message = AZStd::string::format( - "A prefab with the relative path \"%s\" already exists in the Asset Database. \r\n\r\n" - "Overriding it will damage instances or cascades of this prefab. \r\n\r\n" - "Instead, either push entities/fields to the prefab, or save to a different location.", - prefabRelativeName.c_str()); - - WarnUserOfError("Prefab Path Error", message); - return false; - } - } - - AZStd::string saveDir(prefabSaveFileInfo.absoluteDir().absolutePath().toUtf8().constData()); - SetPrefabSaveLocation(saveDir, prefabUserSettingsId); - - outPrefabName = prefabName.toUtf8().constData(); - outPrefabFilePath = targetPath.c_str(); - - return true; } - bool PrefabIntegrationManager::QueryUserForPrefabFilePath(AZStd::string& outPrefabFilePath) - { - AssetSelectionModel selection; - - // Note, stringfilter will match every source file CONTAINING ".prefab". - // If this causes issues, we will need to create a new filter class for regex matching. - // We'll need to check if the file contents are actually a prefab later in the flow anyways, - // so this should not be an issue. - StringFilter* stringFilter = new StringFilter(); - stringFilter->SetName("Prefab"); - stringFilter->SetFilterString(".prefab"); - stringFilter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::Down); - auto stringFilterPtr = FilterConstType(stringFilter); - - EntryTypeFilter* sourceFilter = new EntryTypeFilter(); - sourceFilter->SetName("Source"); - sourceFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Source); - sourceFilter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::Down); - auto sourceFilterPtr = FilterConstType(sourceFilter); - - CompositeFilter* compositeFilter = new CompositeFilter(CompositeFilter::LogicOperatorType::AND); - compositeFilter->SetName("Prefab"); - compositeFilter->AddFilter(sourceFilterPtr); - compositeFilter->AddFilter(stringFilterPtr); - auto compositeFilterPtr = FilterConstType(compositeFilter); - - selection.SetDisplayFilter(compositeFilterPtr); - selection.SetSelectionFilter(compositeFilterPtr); - - AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, AzToolsFramework::GetActiveWindow()); - - if (!selection.IsValid()) - { - // User closed the dialog without selecting, just return. - return false; - } - - auto source = azrtti_cast(selection.GetResult()); - - if (source == nullptr) - { - AZ_Assert(false, "Prefab - Incorrect entry type selected during prefab instantiation. Expected source."); - return false; - } - - outPrefabFilePath = source->GetFullPath(); - return true; - } - - bool PrefabIntegrationManager::QueryUserForProceduralPrefabAsset(AZStd::string& outPrefabAssetPath) - { - using namespace AzToolsFramework; - auto selection = AssetBrowser::AssetSelectionModel::AssetTypeSelection(azrtti_typeid()); - EditorRequests::Bus::Broadcast(&AzToolsFramework::EditorRequests::BrowseForAssets, selection); - - if (!selection.IsValid()) - { - return false; - } - - auto product = azrtti_cast(selection.GetResult()); - if (product == nullptr) - { - return false; - } - outPrefabAssetPath = product->GetRelativePath(); - return true; - } - - void PrefabIntegrationManager::WarnUserOfError(AZStd::string_view title, AZStd::string_view message) - { - QWidget* activeWindow = QApplication::activeWindow(); - - QMessageBox::warning( - activeWindow, - QString(title.data()), - QString(message.data()), - QMessageBox::Ok, - QMessageBox::Ok - ); - } - - PrefabIntegrationManager::PrefabSaveResult PrefabIntegrationManager::IsPrefabPathValidForAssets(QWidget* activeWindow, - QString prefabPath, AZStd::string& retrySavePath) - { - bool assetSetFoldersRetrieved = false; - AZStd::vector assetSafeFolders; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult( - assetSetFoldersRetrieved, - &AzToolsFramework::AssetSystemRequestBus::Events::GetAssetSafeFolders, - assetSafeFolders); - - if (!assetSetFoldersRetrieved) - { - // If the asset safe list couldn't be retrieved, don't block the user but warn them. - AZ_Warning("Prefab", false, "Unable to verify that the prefab file to create is in a valid path."); - } - else - { - AZ::IO::FixedMaxPath lexicallyNormalPath = AZ::IO::PathView(prefabPath.toUtf8().constData()).LexicallyNormal(); - - bool isPathSafeForAssets = false; - for (const AZStd::string& assetSafeFolder : assetSafeFolders) - { - AZ::IO::PathView assetSafeFolderView(assetSafeFolder); - // Check if the prefabPath is relative to the safe asset directory. - // The Path classes are being used to make this check case insensitive. - if (lexicallyNormalPath.IsRelativeTo(assetSafeFolderView)) - { - isPathSafeForAssets = true; - break; - } - } - - if (!isPathSafeForAssets) - { - // Put an error in the console, so the log files have info about this error, or the user can look up the error after dismissing it. - AZStd::string errorMessage = "You can only save prefabs to either your game project folder or the Gems folder. Update the location and try again.\n\n" - "You can also review and update your save locations in the AssetProcessorPlatformConfig.ini file."; - AZ_Error("Prefab", false, errorMessage.c_str()); - - // Display a pop-up, the logs are easy to miss. This will make sure a user who encounters this error immediately knows their prefab save has failed. - QMessageBox msgBox(activeWindow); - msgBox.setIcon(QMessageBox::Icon::Warning); - msgBox.setTextFormat(Qt::RichText); - msgBox.setWindowTitle(QObject::tr("Invalid save location")); - msgBox.setText(QObject::tr(errorMessage.c_str())); - msgBox.setStandardButtons(QMessageBox::Cancel | QMessageBox::Retry); - msgBox.setDefaultButton(QMessageBox::Retry); - const int response = msgBox.exec(); - switch (response) - { - case QMessageBox::Retry: - // If the user wants to retry, they probably want to save to a valid location, - // so set the suggested save path to a known valid location. - if (assetSafeFolders.size() > 0) - { - retrySavePath = assetSafeFolders[0]; - } - return PrefabSaveResult::Retry; - case QMessageBox::Cancel: - default: - return PrefabSaveResult::Cancel; - } - } - } - // Valid prefab save location, continue with the save attempt. - return PrefabSaveResult::Continue; - } - - void PrefabIntegrationManager::GenerateSuggestedPrefabPath(const AZStd::string& prefabName, const AZStd::string& targetDirectory, AZStd::string& suggestedFullPath) - { - // Generate full suggested path from prefabName - if given NewPrefab as prefabName, - // NewPrefab_001.prefab would be tried, and if that already existed we would suggest - // the first unused number value (NewPrefab_002.prefab etc.) - AZStd::string normalizedTargetDirectory = targetDirectory; - AZ::StringFunc::Path::Normalize(normalizedTargetDirectory); - - // Convert spaces in entity names to underscores - AZStd::string prefabNameFiltered = prefabName; - AZ::StringFunc::Replace(prefabNameFiltered, ' ', '_'); - - auto settings = AZ::UserSettings::CreateFind(AZ_CRC("PrefabUserSettings"), AZ::UserSettings::CT_LOCAL); - if (settings->m_autoNumber) - { - AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); - - const AZ::u32 maxPrefabNumber = 1000; - for (AZ::u32 prefabNumber = 1; prefabNumber < maxPrefabNumber; ++prefabNumber) - { - AZStd::string possiblePath; - AZ::StringFunc::Path::Join( - normalizedTargetDirectory.c_str(), - AZStd::string::format("%s_%3.3u%s", prefabNameFiltered.c_str(), prefabNumber, s_prefabFileExtension.c_str()).c_str(), - possiblePath - ); - - if (!fileIO || !fileIO->Exists(possiblePath.c_str())) - { - suggestedFullPath = possiblePath; - break; - } - } - } - else - { - // use the entity name as the file name regardless of it already existing, the OS will ask the user to overwrite the file in that case. - AZ::StringFunc::Path::Join( - normalizedTargetDirectory.c_str(), - AZStd::string::format("%s%s", prefabNameFiltered.c_str(), s_prefabFileExtension.c_str()).c_str(), - suggestedFullPath - ); - } - } - - void PrefabIntegrationManager::SetPrefabSaveLocation(const AZStd::string& path, AZ::u32 settingsId) - { - auto settings = AZ::UserSettings::CreateFind(settingsId, AZ::UserSettings::CT_LOCAL); - settings->m_saveLocation = path; - } - - bool PrefabIntegrationManager::GetPrefabSaveLocation(AZStd::string& path, AZ::u32 settingsId) - { - auto settings = AZ::UserSettings::Find(settingsId, AZ::UserSettings::CT_LOCAL); - if (settings) - { - path = settings->m_saveLocation; - return true; - } - - return false; - } - - void PrefabIntegrationManager::GatherAllReferencedEntitiesAndCompare(const EntityIdSet& entities, - EntityIdSet& entitiesAndReferencedEntities, bool& hasExternalReferences) + void PrefabIntegrationManager::GatherAllReferencedEntitiesAndCompare( + const EntityIdSet& entities, EntityIdSet& entitiesAndReferencedEntities, bool& hasExternalReferences) { AZ::SerializeContext* serializeContext; AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext); @@ -1130,7 +709,8 @@ namespace AzToolsFramework } } - const AZ::Edit::ElementData* classEditData = classData ? classData->FindElementData(AZ::Edit::ClassElements::EditorData) : nullptr; + const AZ::Edit::ElementData* classEditData = + classData ? classData->FindElementData(AZ::Edit::ClassElements::EditorData) : nullptr; if (classEditData) { AZ::Edit::Attribute* slicePushAttribute = classEditData->FindAttribute(AZ::Edit::Attributes::SliceFlags); @@ -1146,7 +726,8 @@ namespace AzToolsFramework return sliceFlags; } - void PrefabIntegrationManager::GatherAllReferencedEntities(EntityIdSet& entitiesWithReferences, AZ::SerializeContext& serializeContext) + void PrefabIntegrationManager::GatherAllReferencedEntities( + EntityIdSet& entitiesWithReferences, AZ::SerializeContext& serializeContext) { AZ_PROFILE_FUNCTION(AzToolsFramework); @@ -1172,18 +753,20 @@ namespace AzToolsFramework { AZStd::vector parentStack; parentStack.reserve(30); - auto beginCB = [&](void* ptr, const AZ::SerializeContext::ClassData* classData, const AZ::SerializeContext::ClassElement* elementData) -> bool + auto beginCB = [&](void* ptr, const AZ::SerializeContext::ClassData* classData, + const AZ::SerializeContext::ClassElement* elementData) -> bool { parentStack.push_back(classData); - AZ::u32 sliceFlags = GetSliceFlags(elementData ? elementData->m_editData : nullptr, classData ? classData->m_editData : nullptr); + AZ::u32 sliceFlags = + GetSliceFlags(elementData ? elementData->m_editData : nullptr, classData ? classData->m_editData : nullptr); // Skip any class or element marked as don't gather references if (0 != (sliceFlags & AZ::Edit::SliceFlags::DontGatherReference)) { return false; } - + if (classData->m_typeId == AZ::SerializeTypeInfo::GetUuid()) { if (!parentStack.empty() && parentStack.back()->m_typeId == AZ::SerializeTypeInfo::GetUuid()) @@ -1192,8 +775,9 @@ namespace AzToolsFramework } else { - AZ::EntityId* entityIdPtr = (elementData->m_flags & AZ::SerializeContext::ClassElement::FLG_POINTER) ? - *reinterpret_cast(ptr) : reinterpret_cast(ptr); + AZ::EntityId* entityIdPtr = (elementData->m_flags & AZ::SerializeContext::ClassElement::FLG_POINTER) + ? *reinterpret_cast(ptr) + : reinterpret_cast(ptr); if (entityIdPtr) { const AZ::EntityId id = *entityIdPtr; @@ -1219,26 +803,15 @@ namespace AzToolsFramework }; AZ::SerializeContext::EnumerateInstanceCallContext callContext( - beginCB, - endCB, - &serializeContext, - AZ::SerializeContext::ENUM_ACCESS_FOR_READ, - nullptr - ); - - serializeContext.EnumerateInstanceConst( - &callContext, - entity, - azrtti_typeid(), - nullptr, - nullptr - ); + beginCB, endCB, &serializeContext, AZ::SerializeContext::ENUM_ACCESS_FOR_READ, nullptr); + + serializeContext.EnumerateInstanceConst(&callContext, entity, azrtti_typeid(), nullptr, nullptr); } } } - bool PrefabIntegrationManager::QueryAndPruneMissingExternalReferences(EntityIdSet& entities, EntityIdSet& selectedAndReferencedEntities, - bool& useReferencedEntities, bool defaultMoveExternalRefs) + bool PrefabIntegrationManager::QueryAndPruneMissingExternalReferences( + EntityIdSet& entities, EntityIdSet& selectedAndReferencedEntities, bool& useReferencedEntities, bool defaultMoveExternalRefs) { AZ_PROFILE_FUNCTION(AzToolsFramework); useReferencedEntities = false; @@ -1278,9 +851,9 @@ namespace AzToolsFramework AZ_PROFILE_FUNCTION(AzToolsFramework); const AZStd::string message = AZStd::string::format( - "Entity references may not be valid if the entity IDs change or if the entities do not exist when the prefab is instantiated.\r\n\r\nSelected Entities\n%s\nReferenced Entities\n%s\n", - includedEntities.c_str(), - referencedEntities.c_str()); + "Entity references may not be valid if the entity IDs change or if the entities do not exist when the prefab is " + "instantiated.\r\n\r\nSelected Entities\n%s\nReferenced Entities\n%s\n", + includedEntities.c_str(), referencedEntities.c_str()); QMessageBox msgBox(AzToolsFramework::GetActiveWindow()); msgBox.setWindowTitle("External Entity References"); @@ -1361,243 +934,21 @@ namespace AzToolsFramework } else { - WarnUserOfError("Entity Creation Error", createResult.GetError()); + WarningDialog("Entity Creation Error", createResult.GetError()); return AZ::EntityId(); } } int PrefabIntegrationManager::ExecuteClosePrefabDialog(TemplateId templateId) { - if (s_prefabSystemComponentInterface->AreDirtyTemplatesPresent(templateId)) - { - auto prefabSaveSelectionDialog = ConstructClosePrefabDialog(templateId); - - int prefabSaveSelection = prefabSaveSelectionDialog->exec(); - - if (prefabSaveSelection == QDialog::Accepted) - { - SavePrefabsInDialog(prefabSaveSelectionDialog.get()); - } - - return prefabSaveSelection; - } - - return QDialogButtonBox::DestructiveRole; + return m_prefabSaveHandler.ExecuteClosePrefabDialog(templateId); } void PrefabIntegrationManager::ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) { - auto prefabTemplate = s_prefabSystemComponentInterface->FindTemplate(templateId); - AZ::IO::Path prefabTemplatePath = prefabTemplate->get().GetFilePath(); - - if (s_prefabSystemComponentInterface->IsTemplateDirty(templateId)) - { - if (s_prefabLoaderInterface->SaveTemplate(templateId) == false) - { - AZ_Error("Prefab", false, "Template '%s' could not be saved successfully.", prefabTemplatePath.c_str()); - return; - } - } - - if (s_prefabSystemComponentInterface->AreDirtyTemplatesPresent(templateId)) - { - if (useSaveAllPrefabsPreference) - { - SaveAllPrefabsPreference saveAllPrefabsPreference = s_prefabLoaderInterface->GetSaveAllPrefabsPreference(); - - if (saveAllPrefabsPreference == SaveAllPrefabsPreference::SaveAll) - { - s_prefabSystemComponentInterface->SaveAllDirtyTemplates(templateId); - return; - } - else if (saveAllPrefabsPreference == SaveAllPrefabsPreference::SaveNone) - { - return; - } - } - - AZStd::unique_ptr savePrefabDialog = ConstructSavePrefabDialog(templateId, useSaveAllPrefabsPreference); - if (savePrefabDialog) - { - int prefabSaveSelection = savePrefabDialog->exec(); - - if (prefabSaveSelection == QDialog::Accepted) - { - SavePrefabsInDialog(savePrefabDialog.get()); - } - } - } - } - - void PrefabIntegrationManager::SavePrefabsInDialog(QDialog* unsavedPrefabsDialog) - { - QList unsavedPrefabFileLabels = unsavedPrefabsDialog->findChildren(UnsavedPrefabFileName); - if (unsavedPrefabFileLabels.size() > 0) - { - for (const QLabel* unsavedPrefabFileLabel : unsavedPrefabFileLabels) - { - AZStd::string unsavedPrefabFileName = unsavedPrefabFileLabel->property("FilePath").toString().toUtf8().data(); - AzToolsFramework::Prefab::TemplateId unsavedPrefabTemplateId = - s_prefabSystemComponentInterface->GetTemplateIdFromFilePath(unsavedPrefabFileName.data()); - [[maybe_unused]] bool isTemplateSavedSuccessfully = s_prefabLoaderInterface->SaveTemplate(unsavedPrefabTemplateId); - AZ_Error("Prefab", isTemplateSavedSuccessfully, "Prefab '%s' could not be saved successfully.", unsavedPrefabFileName.c_str()); - } - } - } - - AZStd::unique_ptr PrefabIntegrationManager::ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) - { - AZStd::unique_ptr savePrefabDialog = AZStd::make_unique(AzToolsFramework::GetActiveWindow()); - - savePrefabDialog->setWindowTitle("Unsaved files detected"); - - // Main Content section begins. - savePrefabDialog->setObjectName(SavePrefabDialog); - QBoxLayout* contentLayout = new QVBoxLayout(savePrefabDialog.get()); - - QFrame* prefabSavedMessageFrame = new QFrame(savePrefabDialog.get()); - QHBoxLayout* prefabSavedMessageLayout = new QHBoxLayout(savePrefabDialog.get()); - prefabSavedMessageFrame->setObjectName(PrefabSavedMessageFrame); - prefabSavedMessageFrame->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); - - // Add a checkMark icon next to the level entities saved message. - QPixmap checkMarkIcon(QString(":/Notifications/checkmark.svg")); - QLabel* prefabSavedSuccessfullyIconContainer = new QLabel(savePrefabDialog.get()); - prefabSavedSuccessfullyIconContainer->setPixmap(checkMarkIcon); - prefabSavedSuccessfullyIconContainer->setFixedWidth(checkMarkIcon.width()); - - // Add a message that level entities are saved successfully. - - auto prefabTemplate = s_prefabSystemComponentInterface->FindTemplate(templateId); - AZ::IO::Path prefabTemplatePath = prefabTemplate->get().GetFilePath(); - QLabel* prefabSavedSuccessfullyLabel = new QLabel( - QString("Prefab '%1' has been saved. Do you want to save the below dependent prefabs too?").arg(prefabTemplatePath.c_str()), - savePrefabDialog.get()); - prefabSavedMessageLayout->addWidget(prefabSavedSuccessfullyIconContainer); - prefabSavedMessageLayout->addWidget(prefabSavedSuccessfullyLabel); - prefabSavedMessageFrame->setLayout(prefabSavedMessageLayout); - contentLayout->addWidget(prefabSavedMessageFrame); - - AZStd::unique_ptr unsavedPrefabsContainer = ConstructUnsavedPrefabsCard(templateId); - contentLayout->addWidget(unsavedPrefabsContainer.release()); - - contentLayout->addStretch(); - - // Footer section begins. - QHBoxLayout* footerLayout = new QHBoxLayout(savePrefabDialog.get()); - - if (useSaveAllPrefabsPreference) - { - QFrame* footerSeparatorLine = new QFrame(savePrefabDialog.get()); - footerSeparatorLine->setObjectName(FooterSeparatorLine); - footerSeparatorLine->setFrameShape(QFrame::HLine); - contentLayout->addWidget(footerSeparatorLine); - - QLabel* prefabSavePreferenceHint = new QLabel( - "You can prevent this window from showing in the future by updating your global save preferences.", - savePrefabDialog.get()); - prefabSavePreferenceHint->setToolTip( - "Go to 'Edit > Editor Settings > Global Preferences... > Global save preferences' to update your preference"); - prefabSavePreferenceHint->setObjectName(PrefabSavePreferenceHint); - footerLayout->addWidget(prefabSavePreferenceHint); - } - - QDialogButtonBox* prefabSaveConfirmationButtons = - new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::No, savePrefabDialog.get()); - footerLayout->addWidget(prefabSaveConfirmationButtons); - contentLayout->addLayout(footerLayout); - connect(prefabSaveConfirmationButtons, &QDialogButtonBox::accepted, savePrefabDialog.get(), &QDialog::accept); - connect(prefabSaveConfirmationButtons, &QDialogButtonBox::rejected, savePrefabDialog.get(), &QDialog::reject); - AzQtComponents::StyleManager::setStyleSheet(savePrefabDialog->parentWidget(), QStringLiteral("style:Editor.qss")); - - savePrefabDialog->setLayout(contentLayout); - return AZStd::move(savePrefabDialog); - } - - AZStd::shared_ptr PrefabIntegrationManager::ConstructClosePrefabDialog(TemplateId templateId) - { - AZStd::shared_ptr closePrefabDialog = AZStd::make_shared(AzToolsFramework::GetActiveWindow()); - closePrefabDialog->setWindowTitle("Unsaved files detected"); - AZStd::weak_ptr closePrefabDialogWeakPtr(closePrefabDialog); - closePrefabDialog->setObjectName(ClosePrefabDialog); - - // Main Content section begins. - QVBoxLayout* contentLayout = new QVBoxLayout(closePrefabDialog.get()); - QFrame* prefabSaveWarningFrame = new QFrame(closePrefabDialog.get()); - QHBoxLayout* levelEntitiesSaveQuestionLayout = new QHBoxLayout(closePrefabDialog.get()); - prefabSaveWarningFrame->setObjectName(PrefabSaveWarningFrame); - - // Add a warning icon next to save prefab warning. - prefabSaveWarningFrame->setLayout(levelEntitiesSaveQuestionLayout); - QPixmap warningIcon(QString(":/Notifications/warning.svg")); - QLabel* warningIconContainer = new QLabel(closePrefabDialog.get()); - warningIconContainer->setPixmap(warningIcon); - warningIconContainer->setFixedWidth(warningIcon.width()); - levelEntitiesSaveQuestionLayout->addWidget(warningIconContainer); - - // Ask user if they want to save entities in level. - QLabel* prefabSaveQuestionLabel = new QLabel("Do you want to save the below unsaved prefabs?", closePrefabDialog.get()); - levelEntitiesSaveQuestionLayout->addWidget(prefabSaveQuestionLabel); - contentLayout->addWidget(prefabSaveWarningFrame); - - auto templateToSave = s_prefabSystemComponentInterface->FindTemplate(templateId); - AZ::IO::Path templateToSaveFilePath = templateToSave->get().GetFilePath(); - AZStd::unique_ptr unsavedPrefabsCard = ConstructUnsavedPrefabsCard(templateId); - contentLayout->addWidget(unsavedPrefabsCard.release()); - - contentLayout->addStretch(); - - QHBoxLayout* footerLayout = new QHBoxLayout(closePrefabDialog.get()); - - QDialogButtonBox* prefabSaveConfirmationButtons = new QDialogButtonBox( - QDialogButtonBox::Save | QDialogButtonBox::Discard | QDialogButtonBox::Cancel, closePrefabDialog.get()); - footerLayout->addWidget(prefabSaveConfirmationButtons); - contentLayout->addLayout(footerLayout); - QObject::connect(prefabSaveConfirmationButtons, &QDialogButtonBox::accepted, closePrefabDialog.get(), &QDialog::accept); - QObject::connect(prefabSaveConfirmationButtons, &QDialogButtonBox::rejected, closePrefabDialog.get(), &QDialog::reject); - QObject::connect( - prefabSaveConfirmationButtons, &QDialogButtonBox::clicked, closePrefabDialog.get(), - [closePrefabDialogWeakPtr, prefabSaveConfirmationButtons](QAbstractButton* button) - { - int prefabSaveSelection = prefabSaveConfirmationButtons->buttonRole(button); - closePrefabDialogWeakPtr.lock()->done(prefabSaveSelection); - }); - AzQtComponents::StyleManager::setStyleSheet(closePrefabDialog.get(), QStringLiteral("style:Editor.qss")); - closePrefabDialog->setLayout(contentLayout); - return closePrefabDialog; + m_prefabSaveHandler.ExecuteSavePrefabDialog(templateId, useSaveAllPrefabsPreference); } - AZStd::unique_ptr PrefabIntegrationManager::ConstructUnsavedPrefabsCard(TemplateId templateId) - { - FlowLayout* unsavedPrefabsLayout = new FlowLayout(nullptr); + } // namespace Prefab - AZStd::set dirtyTemplatePaths = s_prefabSystemComponentInterface->GetDirtyTemplatePaths(templateId); - - for (AZ::IO::PathView dirtyTemplatePath : dirtyTemplatePaths) - { - QLabel* prefabNameLabel = - new QLabel(QString("%1").arg(dirtyTemplatePath.Filename().Native().data()), AzToolsFramework::GetActiveWindow()); - prefabNameLabel->setObjectName(UnsavedPrefabFileName); - prefabNameLabel->setWordWrap(true); - prefabNameLabel->setToolTip(dirtyTemplatePath.Native().data()); - prefabNameLabel->setProperty("FilePath", dirtyTemplatePath.Native().data()); - unsavedPrefabsLayout->addWidget(prefabNameLabel); - } - - AZStd::unique_ptr unsavedPrefabsContainer = AZStd::make_unique(AzToolsFramework::GetActiveWindow()); - unsavedPrefabsContainer->setObjectName(SaveDependentPrefabsCard); - unsavedPrefabsContainer->setTitle("Unsaved Prefabs"); - unsavedPrefabsContainer->header()->setHasContextMenu(false); - unsavedPrefabsContainer->header()->setIcon(QIcon(QStringLiteral(":/Entity/prefab_edit.svg"))); - - QFrame* unsavedPrefabsFrame = new QFrame(unsavedPrefabsContainer.get()); - unsavedPrefabsFrame->setLayout(unsavedPrefabsLayout); - QScrollArea* unsavedPrefabsScrollArea = new QScrollArea(unsavedPrefabsContainer.get()); - unsavedPrefabsScrollArea->setWidget(unsavedPrefabsFrame); - unsavedPrefabsScrollArea->setWidgetResizable(true); - unsavedPrefabsContainer->setContentWidget(unsavedPrefabsScrollArea); - - return AZStd::move(unsavedPrefabsContainer); - } - } -} +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h index 800873349a..bb8fa227a8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h @@ -9,23 +9,18 @@ #pragma once #include -#include #include -#include #include #include -#include -#include #include #include #include +#include #include #include #include -#include - namespace AzToolsFramework { class ContainerEntityInterface; @@ -35,30 +30,13 @@ namespace AzToolsFramework { class PrefabFocusPublicInterface; class PrefabLoaderInterface; - - //! Structure for saving/retrieving user settings related to prefab workflows. - class PrefabUserSettings - : public AZ::UserSettings - { - public: - AZ_CLASS_ALLOCATOR(PrefabUserSettings, AZ::SystemAllocator, 0); - AZ_RTTI(PrefabUserSettings, "{E17A6128-E2C3-4501-B1AD-B8BB0D315602}", AZ::UserSettings); - - AZStd::string m_saveLocation; - bool m_autoNumber = false; //!< Should the name of the prefab file be automatically numbered. e.g PrefabName_001.prefab vs PrefabName.prefab. - - PrefabUserSettings() = default; - - static void Reflect(AZ::ReflectContext* context); - }; + class PrefabPublicInterface; class PrefabIntegrationManager final : public EditorContextMenuBus::Handler , public EditorEventsBus::Handler - , public AssetBrowser::AssetBrowserSourceDropBus::Handler , public PrefabInstanceContainerNotificationBus::Handler , public PrefabIntegrationInterface - , public QObject , private EditorEntityContextNotificationBus::Handler { public: @@ -77,9 +55,6 @@ namespace AzToolsFramework // EditorEventsBus overrides ... void OnEscape() override; - // EntityOutlinerSourceDropHandlingBus overrides ... - void HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const override; - // EditorEntityContextNotificationBus overrides ... void OnStartPlayInEditorBegin() override; void OnStopPlayInEditor() override; @@ -94,6 +69,9 @@ namespace AzToolsFramework void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override; private: + // Handles the UI for prefab save operations. + PrefabSaveHandler m_prefabSaveHandler; + // Used to handle the UI for the level root. LevelRootUiHandler m_levelRootUiHandler; @@ -120,27 +98,6 @@ namespace AzToolsFramework void InitializeShortcuts(); void UninitializeShortcuts(); - // Prompt and resolve dialogs - static bool QueryUserForPrefabSaveLocation( - const AZStd::string& suggestedName, const char* initialTargetDirectory, AZ::u32 prefabUserSettingsId, QWidget* activeWindow, - AZStd::string& outPrefabName, AZStd::string& outPrefabFilePath); - static bool QueryUserForPrefabFilePath(AZStd::string& outPrefabFilePath); - static bool QueryUserForProceduralPrefabAsset(AZStd::string& outPrefabAssetPath); - static void WarnUserOfError(AZStd::string_view title, AZStd::string_view message); - - // Path and filename generation - static void GenerateSuggestedFilenameFromEntities(const EntityIdList& entities, AZStd::string& outName); - static bool AppendEntityToSuggestedFilename(AZStd::string& filename, AZ::EntityId entityId); - - enum class PrefabSaveResult - { - Continue, - Retry, - Cancel - }; - static PrefabSaveResult IsPrefabPathValidForAssets(QWidget* activeWindow, QString prefabPath, AZStd::string& retrySavePath); - static void GenerateSuggestedPrefabPath(const AZStd::string& prefabName, const AZStd::string& targetDirectory, AZStd::string& suggestedFullPath); - // Reference detection static void GatherAllReferencedEntitiesAndCompare(const EntityIdSet& entities, EntityIdSet& entitiesAndReferencedEntities, bool& hasExternalReferences); @@ -148,20 +105,10 @@ namespace AzToolsFramework static bool QueryAndPruneMissingExternalReferences(EntityIdSet& entities, EntityIdSet& selectedAndReferencedEntities, bool& useReferencedEntities, bool defaultMoveExternalRefs = false); - // Settings management - static void SetPrefabSaveLocation(const AZStd::string& path, AZ::u32 settingsId); - static bool GetPrefabSaveLocation(AZStd::string& path, AZ::u32 settingsId); - static AZ::u32 GetSliceFlags(const AZ::Edit::ElementData* editData, const AZ::Edit::ClassData* classData); - AZStd::shared_ptr ConstructClosePrefabDialog(TemplateId templateId); - AZStd::unique_ptr ConstructUnsavedPrefabsCard(TemplateId templateId); - AZStd::unique_ptr ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference); - void SavePrefabsInDialog(QDialog* unsavedPrefabsDialog); - AZStd::vector> m_actions; - static const AZStd::string s_prefabFileExtension; static AzFramework::EntityContextId s_editorEntityContextId; static ContainerEntityInterface* s_containerEntityInterface; @@ -169,7 +116,6 @@ namespace AzToolsFramework static PrefabFocusPublicInterface* s_prefabFocusPublicInterface; static PrefabLoaderInterface* s_prefabLoaderInterface; static PrefabPublicInterface* s_prefabPublicInterface; - static PrefabSystemComponentInterface* s_prefabSystemComponentInterface; ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabSaveLoadHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabSaveLoadHandler.cpp new file mode 100644 index 0000000000..7234165076 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabSaveLoadHandler.cpp @@ -0,0 +1,722 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AzToolsFramework +{ + namespace Prefab + { + PrefabLoaderInterface* PrefabSaveHandler::s_prefabLoaderInterface = nullptr; + PrefabPublicInterface* PrefabSaveHandler::s_prefabPublicInterface = nullptr; + PrefabSystemComponentInterface* PrefabSaveHandler::s_prefabSystemComponentInterface = nullptr; + + const AZStd::string PrefabSaveHandler::s_prefabFileExtension = ".prefab"; + + static const char* const ClosePrefabDialog = "ClosePrefabDialog"; + static const char* const FooterSeparatorLine = "FooterSeparatorLine"; + static const char* const PrefabSavedMessageFrame = "PrefabSavedMessageFrame"; + static const char* const PrefabSavePreferenceHint = "PrefabSavePreferenceHint"; + static const char* const PrefabSaveWarningFrame = "PrefabSaveWarningFrame"; + static const char* const SaveDependentPrefabsCard = "SaveDependentPrefabsCard"; + static const char* const SavePrefabDialog = "SavePrefabDialog"; + static const char* const UnsavedPrefabFileName = "UnsavedPrefabFileName"; + + void PrefabUserSettings::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1) + ->Field("m_saveLocation", &PrefabUserSettings::m_saveLocation) + ->Field("m_autoNumber", &PrefabUserSettings::m_autoNumber); + } + } + + PrefabSaveHandler::PrefabSaveHandler() + { + s_prefabLoaderInterface = AZ::Interface::Get(); + if (s_prefabLoaderInterface == nullptr) + { + AZ_Assert(false, "Prefab - could not get PrefabLoaderInterface on PrefabSaveHandler construction."); + return; + } + + s_prefabPublicInterface = AZ::Interface::Get(); + if (s_prefabPublicInterface == nullptr) + { + AZ_Assert(false, "Prefab - could not get PrefabPublicInterface on PrefabSaveHandler construction."); + return; + } + + s_prefabSystemComponentInterface = AZ::Interface::Get(); + if (s_prefabSystemComponentInterface == nullptr) + { + AZ_Assert(false, "Prefab - could not get PrefabSystemComponentInterface on PrefabSaveHandler construction."); + return; + } + + AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension); + } + + PrefabSaveHandler::~PrefabSaveHandler() + { + AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect(); + } + + bool PrefabSaveHandler::GetPrefabSaveLocation(AZStd::string& path, AZ::u32 settingsId) + { + auto settings = AZ::UserSettings::Find(settingsId, AZ::UserSettings::CT_LOCAL); + if (settings) + { + path = settings->m_saveLocation; + return true; + } + + return false; + } + + void PrefabSaveHandler::SetPrefabSaveLocation(const AZStd::string& path, AZ::u32 settingsId) + { + auto settings = AZ::UserSettings::CreateFind(settingsId, AZ::UserSettings::CT_LOCAL); + settings->m_saveLocation = path; + } + + void PrefabSaveHandler::HandleSourceFileType( + AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const + { + auto instantiatePrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(sourceFilePath, parentId, position); + + if (!instantiatePrefabOutcome.IsSuccess()) + { + WarningDialog("Prefab Instantiation Error", instantiatePrefabOutcome.GetError()); + } + } + + int PrefabSaveHandler::ExecuteClosePrefabDialog(TemplateId templateId) + { + if (s_prefabSystemComponentInterface->AreDirtyTemplatesPresent(templateId)) + { + auto prefabSaveSelectionDialog = ConstructClosePrefabDialog(templateId); + + int prefabSaveSelection = prefabSaveSelectionDialog->exec(); + + if (prefabSaveSelection == QDialog::Accepted) + { + SavePrefabsInDialog(prefabSaveSelectionDialog.get()); + } + + return prefabSaveSelection; + } + + return QDialogButtonBox::DestructiveRole; + } + + void PrefabSaveHandler::ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) + { + auto prefabTemplate = s_prefabSystemComponentInterface->FindTemplate(templateId); + AZ::IO::Path prefabTemplatePath = prefabTemplate->get().GetFilePath(); + + if (s_prefabSystemComponentInterface->IsTemplateDirty(templateId)) + { + if (s_prefabLoaderInterface->SaveTemplate(templateId) == false) + { + AZ_Error("Prefab", false, "Template '%s' could not be saved successfully.", prefabTemplatePath.c_str()); + return; + } + } + + if (s_prefabSystemComponentInterface->AreDirtyTemplatesPresent(templateId)) + { + if (useSaveAllPrefabsPreference) + { + SaveAllPrefabsPreference saveAllPrefabsPreference = s_prefabLoaderInterface->GetSaveAllPrefabsPreference(); + + if (saveAllPrefabsPreference == SaveAllPrefabsPreference::SaveAll) + { + s_prefabSystemComponentInterface->SaveAllDirtyTemplates(templateId); + return; + } + else if (saveAllPrefabsPreference == SaveAllPrefabsPreference::SaveNone) + { + return; + } + } + + AZStd::unique_ptr savePrefabDialog = ConstructSavePrefabDialog(templateId, useSaveAllPrefabsPreference); + if (savePrefabDialog) + { + int prefabSaveSelection = savePrefabDialog->exec(); + + if (prefabSaveSelection == QDialog::Accepted) + { + SavePrefabsInDialog(savePrefabDialog.get()); + } + } + } + } + + bool PrefabSaveHandler::QueryUserForPrefabFilePath(AZStd::string& outPrefabFilePath) + { + AssetSelectionModel selection; + + // Note, string filter will match every source file CONTAINING ".prefab". + // If this causes issues, we will need to create a new filter class for regex matching. + // We'll need to check if the file contents are actually a prefab later in the flow anyways, + // so this should not be an issue. + StringFilter* stringFilter = new StringFilter(); + stringFilter->SetName("Prefab"); + stringFilter->SetFilterString(".prefab"); + stringFilter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::Down); + auto stringFilterPtr = FilterConstType(stringFilter); + + EntryTypeFilter* sourceFilter = new EntryTypeFilter(); + sourceFilter->SetName("Source"); + sourceFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Source); + sourceFilter->SetFilterPropagation(AssetBrowserEntryFilter::PropagateDirection::Down); + auto sourceFilterPtr = FilterConstType(sourceFilter); + + CompositeFilter* compositeFilter = new CompositeFilter(CompositeFilter::LogicOperatorType::AND); + compositeFilter->SetName("Prefab"); + compositeFilter->AddFilter(sourceFilterPtr); + compositeFilter->AddFilter(stringFilterPtr); + auto compositeFilterPtr = FilterConstType(compositeFilter); + + selection.SetDisplayFilter(compositeFilterPtr); + selection.SetSelectionFilter(compositeFilterPtr); + + AssetBrowserComponentRequestBus::Broadcast( + &AssetBrowserComponentRequests::PickAssets, selection, AzToolsFramework::GetActiveWindow()); + + if (!selection.IsValid()) + { + // User closed the dialog without selecting, just return. + return false; + } + + auto source = azrtti_cast(selection.GetResult()); + + if (source == nullptr) + { + AZ_Assert(false, "Prefab - Incorrect entry type selected during prefab instantiation. Expected source."); + return false; + } + + outPrefabFilePath = source->GetFullPath(); + return true; + } + + bool PrefabSaveHandler::QueryUserForProceduralPrefabAsset(AZStd::string& outPrefabAssetPath) + { + using namespace AzToolsFramework; + auto selection = AssetBrowser::AssetSelectionModel::AssetTypeSelection(azrtti_typeid()); + EditorRequests::Bus::Broadcast(&AzToolsFramework::EditorRequests::BrowseForAssets, selection); + + if (!selection.IsValid()) + { + return false; + } + + auto product = azrtti_cast(selection.GetResult()); + if (product == nullptr) + { + return false; + } + outPrefabAssetPath = product->GetRelativePath(); + return true; + } + + PrefabSaveHandler::PrefabSaveResult PrefabSaveHandler::IsPrefabPathValidForAssets( + QWidget* activeWindow, QString prefabPath, AZStd::string& retrySavePath) + { + bool assetSetFoldersRetrieved = false; + AZStd::vector assetSafeFolders; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + assetSetFoldersRetrieved, &AzToolsFramework::AssetSystemRequestBus::Events::GetAssetSafeFolders, assetSafeFolders); + + if (!assetSetFoldersRetrieved) + { + // If the asset safe list couldn't be retrieved, don't block the user but warn them. + AZ_Warning("Prefab", false, "Unable to verify that the prefab file to create is in a valid path."); + } + else + { + AZ::IO::FixedMaxPath lexicallyNormalPath = AZ::IO::PathView(prefabPath.toUtf8().constData()).LexicallyNormal(); + + bool isPathSafeForAssets = false; + for (const AZStd::string& assetSafeFolder : assetSafeFolders) + { + AZ::IO::PathView assetSafeFolderView(assetSafeFolder); + // Check if the prefabPath is relative to the safe asset directory. + // The Path classes are being used to make this check case insensitive. + if (lexicallyNormalPath.IsRelativeTo(assetSafeFolderView)) + { + isPathSafeForAssets = true; + break; + } + } + + if (!isPathSafeForAssets) + { + // Put an error in the console, so the log files have info about this error, or the user can look up the error after + // dismissing it. + AZStd::string errorMessage = + "You can only save prefabs to either your game project folder or the Gems folder. Update the location and try " + "again.\n\n" + "You can also review and update your save locations in the AssetProcessorPlatformConfig.ini file."; + AZ_Error("Prefab", false, errorMessage.c_str()); + + // Display a pop-up, the logs are easy to miss. This will make sure a user who encounters this error immediately knows + // their prefab save has failed. + QMessageBox msgBox(activeWindow); + msgBox.setIcon(QMessageBox::Icon::Warning); + msgBox.setTextFormat(Qt::RichText); + msgBox.setWindowTitle(QObject::tr("Invalid save location")); + msgBox.setText(QObject::tr(errorMessage.c_str())); + msgBox.setStandardButtons(QMessageBox::Cancel | QMessageBox::Retry); + msgBox.setDefaultButton(QMessageBox::Retry); + const int response = msgBox.exec(); + switch (response) + { + case QMessageBox::Retry: + // If the user wants to retry, they probably want to save to a valid location, + // so set the suggested save path to a known valid location. + if (assetSafeFolders.size() > 0) + { + retrySavePath = assetSafeFolders[0]; + } + return PrefabSaveResult::Retry; + case QMessageBox::Cancel: + default: + return PrefabSaveResult::Cancel; + } + } + } + // Valid prefab save location, continue with the save attempt. + return PrefabSaveResult::Continue; + } + + void PrefabSaveHandler::GenerateSuggestedPrefabPath( + const AZStd::string& prefabName, const AZStd::string& targetDirectory, AZStd::string& suggestedFullPath) + { + // Generate full suggested path from prefabName - if given NewPrefab as prefabName, + // NewPrefab_001.prefab would be tried, and if that already existed we would suggest + // the first unused number value (NewPrefab_002.prefab etc.) + AZStd::string normalizedTargetDirectory = targetDirectory; + AZ::StringFunc::Path::Normalize(normalizedTargetDirectory); + + // Convert spaces in entity names to underscores + AZStd::string prefabNameFiltered = prefabName; + AZ::StringFunc::Replace(prefabNameFiltered, ' ', '_'); + + auto settings = AZ::UserSettings::CreateFind(AZ_CRC("PrefabUserSettings"), AZ::UserSettings::CT_LOCAL); + if (settings->m_autoNumber) + { + AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); + + const AZ::u32 maxPrefabNumber = 1000; + for (AZ::u32 prefabNumber = 1; prefabNumber < maxPrefabNumber; ++prefabNumber) + { + AZStd::string possiblePath; + AZ::StringFunc::Path::Join( + normalizedTargetDirectory.c_str(), + AZStd::string::format("%s_%3.3u%s", prefabNameFiltered.c_str(), prefabNumber, s_prefabFileExtension.c_str()) + .c_str(), + possiblePath); + + if (!fileIO || !fileIO->Exists(possiblePath.c_str())) + { + suggestedFullPath = possiblePath; + break; + } + } + } + else + { + // use the entity name as the file name regardless of it already existing, the OS will ask the user to overwrite the file in + // that case. + AZ::StringFunc::Path::Join( + normalizedTargetDirectory.c_str(), + AZStd::string::format("%s%s", prefabNameFiltered.c_str(), s_prefabFileExtension.c_str()).c_str(), suggestedFullPath); + } + } + + bool PrefabSaveHandler::QueryUserForPrefabSaveLocation( + const AZStd::string& suggestedName, + const char* initialTargetDirectory, + AZ::u32 prefabUserSettingsId, + QWidget* activeWindow, + AZStd::string& outPrefabName, + AZStd::string& outPrefabFilePath) + { + AZStd::string saveAsInitialSuggestedDirectory; + if (!GetPrefabSaveLocation(saveAsInitialSuggestedDirectory, prefabUserSettingsId)) + { + saveAsInitialSuggestedDirectory = initialTargetDirectory; + } + + AZStd::string saveAsInitialSuggestedFullPath; + GenerateSuggestedPrefabPath(suggestedName, saveAsInitialSuggestedDirectory, saveAsInitialSuggestedFullPath); + + QString saveAs; + AZStd::string targetPath; + QFileInfo prefabSaveFileInfo; + QString prefabName; + while (true) + { + { + AZ_PROFILE_FUNCTION(AzToolsFramework); + saveAs = QFileDialog::getSaveFileName( + nullptr, QString("Save As..."), saveAsInitialSuggestedFullPath.c_str(), QString("Prefabs (*.prefab)")); + } + + prefabSaveFileInfo = saveAs; + prefabName = prefabSaveFileInfo.baseName(); + if (saveAs.isEmpty()) + { + return false; + } + + targetPath = saveAs.toUtf8().constData(); + if (AzFramework::StringFunc::Utf8::CheckNonAsciiChar(targetPath)) + { + WarningDialog( + "Prefab Creation Failed.", + "Unicode file name is not supported. \r\n" + "Please use ASCII characters to name your prefab."); + return false; + } + + PrefabSaveResult saveResult = IsPrefabPathValidForAssets(activeWindow, saveAs, saveAsInitialSuggestedFullPath); + if (saveResult == PrefabSaveResult::Cancel) + { + // The error was already reported if this failed. + return false; + } + else if (saveResult == PrefabSaveResult::Continue) + { + // The prefab save name is valid, continue with the save attempt. + break; + } + } + + // If the prefab already exists, notify the user and bail + AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); + if (fileIO && fileIO->Exists(targetPath.c_str())) + { + const AZStd::string message = AZStd::string::format( + "You are attempting to overwrite an existing prefab: \"%s\".\r\n\r\n" + "This will damage instances or cascades of this prefab. \r\n\r\n" + "Instead, either push entities/fields to the prefab, or save to a different location.", + targetPath.c_str()); + + WarningDialog("Prefab Already Exists", message); + return false; + } + + // We prevent users from creating a new prefab with the same relative path that's already + // been used by an existing prefab in other places (e.g. Gems) because the AssetProcessor + // generates asset ids based on relative paths. This is unnecessary once AssetProcessor + // starts to generate UUID to every asset regardless of paths. + { + AZStd::string prefabRelativeName; + bool relativePathFound; + AssetSystemRequestBus::BroadcastResult( + relativePathFound, &AssetSystemRequestBus::Events::GetRelativeProductPathFromFullSourceOrProductPath, targetPath, + prefabRelativeName); + + AZ::Data::AssetId prefabAssetId; + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + prefabAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, prefabRelativeName.c_str(), + AZ::Data::s_invalidAssetType, false); + if (prefabAssetId.IsValid()) + { + const AZStd::string message = AZStd::string::format( + "A prefab with the relative path \"%s\" already exists in the Asset Database. \r\n\r\n" + "Overriding it will damage instances or cascades of this prefab. \r\n\r\n" + "Instead, either push entities/fields to the prefab, or save to a different location.", + prefabRelativeName.c_str()); + + WarningDialog("Prefab Path Error", message); + return false; + } + } + + AZStd::string saveDir(prefabSaveFileInfo.absoluteDir().absolutePath().toUtf8().constData()); + SetPrefabSaveLocation(saveDir, prefabUserSettingsId); + + outPrefabName = prefabName.toUtf8().constData(); + outPrefabFilePath = targetPath.c_str(); + + return true; + } + + void PrefabSaveHandler::GenerateSuggestedFilenameFromEntities(const EntityIdList& entityIds, AZStd::string& outName) + { + AZ_PROFILE_FUNCTION(AzToolsFramework); + + AZStd::string suggestedName; + + for (const AZ::EntityId& entityId : entityIds) + { + if (!AppendEntityToSuggestedFilename(suggestedName, entityId)) + { + break; + } + } + + if (suggestedName.size() == 0 || AzFramework::StringFunc::Utf8::CheckNonAsciiChar(suggestedName)) + { + suggestedName = "NewPrefab"; + } + + outName = suggestedName; + } + + bool PrefabSaveHandler::AppendEntityToSuggestedFilename(AZStd::string& filename, AZ::EntityId entityId) + { + // When naming a prefab after its entities, we stop appending additional names once we've reached this cutoff length + size_t prefabNameCutoffLength = 32; + AzToolsFramework::EntityIdSet usedNameEntities; + + if (usedNameEntities.find(entityId) == usedNameEntities.end()) + { + AZ::Entity* entity = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, entityId); + if (entity) + { + AZStd::string entityNameFiltered = entity->GetName(); + + // Convert spaces in entity names to underscores + for (size_t i = 0; i < entityNameFiltered.size(); ++i) + { + char& character = entityNameFiltered.at(i); + if (character == ' ') + { + character = '_'; + } + } + + filename.append(entityNameFiltered); + usedNameEntities.insert(entityId); + if (filename.size() > prefabNameCutoffLength) + { + return false; + } + } + } + + return true; + } + + void PrefabSaveHandler::SavePrefabsInDialog(QDialog* unsavedPrefabsDialog) + { + QList unsavedPrefabFileLabels = unsavedPrefabsDialog->findChildren(UnsavedPrefabFileName); + if (unsavedPrefabFileLabels.size() > 0) + { + for (const QLabel* unsavedPrefabFileLabel : unsavedPrefabFileLabels) + { + AZStd::string unsavedPrefabFileName = unsavedPrefabFileLabel->property("FilePath").toString().toUtf8().data(); + AzToolsFramework::Prefab::TemplateId unsavedPrefabTemplateId = + s_prefabSystemComponentInterface->GetTemplateIdFromFilePath(unsavedPrefabFileName.data()); + [[maybe_unused]] bool isTemplateSavedSuccessfully = s_prefabLoaderInterface->SaveTemplate(unsavedPrefabTemplateId); + AZ_Error("Prefab", isTemplateSavedSuccessfully, "Prefab '%s' could not be saved successfully.", unsavedPrefabFileName.c_str()); + } + } + } + + AZStd::unique_ptr PrefabSaveHandler::ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) + { + AZStd::unique_ptr savePrefabDialog = AZStd::make_unique(AzToolsFramework::GetActiveWindow()); + + savePrefabDialog->setWindowTitle("Unsaved files detected"); + + // Main Content section begins. + savePrefabDialog->setObjectName(SavePrefabDialog); + QBoxLayout* contentLayout = new QVBoxLayout(savePrefabDialog.get()); + + QFrame* prefabSavedMessageFrame = new QFrame(savePrefabDialog.get()); + QHBoxLayout* prefabSavedMessageLayout = new QHBoxLayout(savePrefabDialog.get()); + prefabSavedMessageFrame->setObjectName(PrefabSavedMessageFrame); + prefabSavedMessageFrame->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + + // Add a checkMark icon next to the level entities saved message. + QPixmap checkMarkIcon(QString(":/Notifications/checkmark.svg")); + QLabel* prefabSavedSuccessfullyIconContainer = new QLabel(savePrefabDialog.get()); + prefabSavedSuccessfullyIconContainer->setPixmap(checkMarkIcon); + prefabSavedSuccessfullyIconContainer->setFixedWidth(checkMarkIcon.width()); + + // Add a message that level entities are saved successfully. + + auto prefabTemplate = s_prefabSystemComponentInterface->FindTemplate(templateId); + AZ::IO::Path prefabTemplatePath = prefabTemplate->get().GetFilePath(); + QLabel* prefabSavedSuccessfullyLabel = new QLabel( + QString("Prefab '%1' has been saved. Do you want to save the below dependent prefabs too?").arg(prefabTemplatePath.c_str()), + savePrefabDialog.get()); + prefabSavedMessageLayout->addWidget(prefabSavedSuccessfullyIconContainer); + prefabSavedMessageLayout->addWidget(prefabSavedSuccessfullyLabel); + prefabSavedMessageFrame->setLayout(prefabSavedMessageLayout); + contentLayout->addWidget(prefabSavedMessageFrame); + + AZStd::unique_ptr unsavedPrefabsContainer = ConstructUnsavedPrefabsCard(templateId); + contentLayout->addWidget(unsavedPrefabsContainer.release()); + + contentLayout->addStretch(); + + // Footer section begins. + QHBoxLayout* footerLayout = new QHBoxLayout(savePrefabDialog.get()); + + if (useSaveAllPrefabsPreference) + { + QFrame* footerSeparatorLine = new QFrame(savePrefabDialog.get()); + footerSeparatorLine->setObjectName(FooterSeparatorLine); + footerSeparatorLine->setFrameShape(QFrame::HLine); + contentLayout->addWidget(footerSeparatorLine); + + QLabel* prefabSavePreferenceHint = new QLabel( + "You can prevent this window from showing in the future by updating your global save preferences.", + savePrefabDialog.get()); + prefabSavePreferenceHint->setToolTip( + "Go to 'Edit > Editor Settings > Global Preferences... > Global save preferences' to update your preference"); + prefabSavePreferenceHint->setObjectName(PrefabSavePreferenceHint); + footerLayout->addWidget(prefabSavePreferenceHint); + } + + QDialogButtonBox* prefabSaveConfirmationButtons = + new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::No, savePrefabDialog.get()); + footerLayout->addWidget(prefabSaveConfirmationButtons); + contentLayout->addLayout(footerLayout); + connect(prefabSaveConfirmationButtons, &QDialogButtonBox::accepted, savePrefabDialog.get(), &QDialog::accept); + connect(prefabSaveConfirmationButtons, &QDialogButtonBox::rejected, savePrefabDialog.get(), &QDialog::reject); + AzQtComponents::StyleManager::setStyleSheet(savePrefabDialog->parentWidget(), QStringLiteral("style:Editor.qss")); + + savePrefabDialog->setLayout(contentLayout); + return AZStd::move(savePrefabDialog); + } + + AZStd::shared_ptr PrefabSaveHandler::ConstructClosePrefabDialog(TemplateId templateId) + { + AZStd::shared_ptr closePrefabDialog = AZStd::make_shared(AzToolsFramework::GetActiveWindow()); + closePrefabDialog->setWindowTitle("Unsaved files detected"); + AZStd::weak_ptr closePrefabDialogWeakPtr(closePrefabDialog); + closePrefabDialog->setObjectName(ClosePrefabDialog); + + // Main Content section begins. + QVBoxLayout* contentLayout = new QVBoxLayout(closePrefabDialog.get()); + QFrame* prefabSaveWarningFrame = new QFrame(closePrefabDialog.get()); + QHBoxLayout* levelEntitiesSaveQuestionLayout = new QHBoxLayout(closePrefabDialog.get()); + prefabSaveWarningFrame->setObjectName(PrefabSaveWarningFrame); + + // Add a warning icon next to save prefab warning. + prefabSaveWarningFrame->setLayout(levelEntitiesSaveQuestionLayout); + QPixmap warningIcon(QString(":/Notifications/warning.svg")); + QLabel* warningIconContainer = new QLabel(closePrefabDialog.get()); + warningIconContainer->setPixmap(warningIcon); + warningIconContainer->setFixedWidth(warningIcon.width()); + levelEntitiesSaveQuestionLayout->addWidget(warningIconContainer); + + // Ask user if they want to save entities in level. + QLabel* prefabSaveQuestionLabel = new QLabel("Do you want to save the below unsaved prefabs?", closePrefabDialog.get()); + levelEntitiesSaveQuestionLayout->addWidget(prefabSaveQuestionLabel); + contentLayout->addWidget(prefabSaveWarningFrame); + + auto templateToSave = s_prefabSystemComponentInterface->FindTemplate(templateId); + AZ::IO::Path templateToSaveFilePath = templateToSave->get().GetFilePath(); + AZStd::unique_ptr unsavedPrefabsCard = ConstructUnsavedPrefabsCard(templateId); + contentLayout->addWidget(unsavedPrefabsCard.release()); + + contentLayout->addStretch(); + + QHBoxLayout* footerLayout = new QHBoxLayout(closePrefabDialog.get()); + + QDialogButtonBox* prefabSaveConfirmationButtons = new QDialogButtonBox( + QDialogButtonBox::Save | QDialogButtonBox::Discard | QDialogButtonBox::Cancel, closePrefabDialog.get()); + footerLayout->addWidget(prefabSaveConfirmationButtons); + contentLayout->addLayout(footerLayout); + QObject::connect(prefabSaveConfirmationButtons, &QDialogButtonBox::accepted, closePrefabDialog.get(), &QDialog::accept); + QObject::connect(prefabSaveConfirmationButtons, &QDialogButtonBox::rejected, closePrefabDialog.get(), &QDialog::reject); + QObject::connect( + prefabSaveConfirmationButtons, &QDialogButtonBox::clicked, closePrefabDialog.get(), + [closePrefabDialogWeakPtr, prefabSaveConfirmationButtons](QAbstractButton* button) + { + int prefabSaveSelection = prefabSaveConfirmationButtons->buttonRole(button); + closePrefabDialogWeakPtr.lock()->done(prefabSaveSelection); + }); + AzQtComponents::StyleManager::setStyleSheet(closePrefabDialog.get(), QStringLiteral("style:Editor.qss")); + closePrefabDialog->setLayout(contentLayout); + return closePrefabDialog; + } + + AZStd::unique_ptr PrefabSaveHandler::ConstructUnsavedPrefabsCard(TemplateId templateId) + { + FlowLayout* unsavedPrefabsLayout = new FlowLayout(nullptr); + + AZStd::set dirtyTemplatePaths = s_prefabSystemComponentInterface->GetDirtyTemplatePaths(templateId); + + for (AZ::IO::PathView dirtyTemplatePath : dirtyTemplatePaths) + { + QLabel* prefabNameLabel = + new QLabel(QString("%1").arg(dirtyTemplatePath.Filename().Native().data()), AzToolsFramework::GetActiveWindow()); + prefabNameLabel->setObjectName(UnsavedPrefabFileName); + prefabNameLabel->setWordWrap(true); + prefabNameLabel->setToolTip(dirtyTemplatePath.Native().data()); + prefabNameLabel->setProperty("FilePath", dirtyTemplatePath.Native().data()); + unsavedPrefabsLayout->addWidget(prefabNameLabel); + } + + AZStd::unique_ptr unsavedPrefabsContainer = AZStd::make_unique(AzToolsFramework::GetActiveWindow()); + unsavedPrefabsContainer->setObjectName(SaveDependentPrefabsCard); + unsavedPrefabsContainer->setTitle("Unsaved Prefabs"); + unsavedPrefabsContainer->header()->setHasContextMenu(false); + unsavedPrefabsContainer->header()->setIcon(QIcon(QStringLiteral(":/Entity/prefab_edit.svg"))); + + QFrame* unsavedPrefabsFrame = new QFrame(unsavedPrefabsContainer.get()); + unsavedPrefabsFrame->setLayout(unsavedPrefabsLayout); + QScrollArea* unsavedPrefabsScrollArea = new QScrollArea(unsavedPrefabsContainer.get()); + unsavedPrefabsScrollArea->setWidget(unsavedPrefabsFrame); + unsavedPrefabsScrollArea->setWidgetResizable(true); + unsavedPrefabsContainer->setContentWidget(unsavedPrefabsScrollArea); + + return AZStd::move(unsavedPrefabsContainer); + } + } +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabSaveLoadHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabSaveLoadHandler.h new file mode 100644 index 0000000000..0a7cd2c582 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabSaveLoadHandler.h @@ -0,0 +1,101 @@ +/* + * 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 AzToolsFramework +{ + namespace Prefab + { + class PrefabLoaderInterface; + class PrefabPublicInterface; + class PrefabSystemComponentInterface; + + //! Structure for saving/retrieving user settings related to prefab workflows. + class PrefabUserSettings : public AZ::UserSettings + { + public: + AZ_CLASS_ALLOCATOR(PrefabUserSettings, AZ::SystemAllocator, 0); + AZ_RTTI(PrefabUserSettings, "{E17A6128-E2C3-4501-B1AD-B8BB0D315602}", AZ::UserSettings); + + AZStd::string m_saveLocation; + bool m_autoNumber = + false; //!< Should the name of the prefab file be automatically numbered. e.g PrefabName_001.prefab vs PrefabName.prefab. + + PrefabUserSettings() = default; + + static void Reflect(AZ::ReflectContext* context); + }; + + //! Class to handle dialogs and windows related to prefab save operations. + class PrefabSaveHandler final + : public QObject + , public AssetBrowser::AssetBrowserSourceDropBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(PrefabSaveHandler, AZ::SystemAllocator, 0); + + PrefabSaveHandler(); + ~PrefabSaveHandler(); + + // Settings management + static bool GetPrefabSaveLocation(AZStd::string& path, AZ::u32 settingsId); + static void SetPrefabSaveLocation(const AZStd::string& path, AZ::u32 settingsId); + + // EntityOutlinerSourceDropHandlingBus overrides ... + void HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const override; + + // Dialogs + int ExecuteClosePrefabDialog(TemplateId templateId); + void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference); + static bool QueryUserForPrefabFilePath(AZStd::string& outPrefabFilePath); + static bool QueryUserForProceduralPrefabAsset(AZStd::string& outPrefabAssetPath); + static bool QueryUserForPrefabSaveLocation( + const AZStd::string& suggestedName, + const char* initialTargetDirectory, + AZ::u32 prefabUserSettingsId, + QWidget* activeWindow, + AZStd::string& outPrefabName, + AZStd::string& outPrefabFilePath); + + // Path and filename generation + enum class PrefabSaveResult + { + Continue, + Retry, + Cancel + }; + + static void GenerateSuggestedFilenameFromEntities(const EntityIdList& entities, AZStd::string& outName); + static bool AppendEntityToSuggestedFilename(AZStd::string& filename, AZ::EntityId entityId); + static PrefabSaveResult IsPrefabPathValidForAssets(QWidget* activeWindow, QString prefabPath, AZStd::string& retrySavePath); + static void GenerateSuggestedPrefabPath( + const AZStd::string& prefabName, const AZStd::string& targetDirectory, AZStd::string& suggestedFullPath); + + private: + AZStd::shared_ptr ConstructClosePrefabDialog(TemplateId templateId); + AZStd::unique_ptr ConstructUnsavedPrefabsCard(TemplateId templateId); + AZStd::unique_ptr ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference); + void SavePrefabsInDialog(QDialog* unsavedPrefabsDialog); + + static const AZStd::string s_prefabFileExtension; + + static PrefabLoaderInterface* s_prefabLoaderInterface; + static PrefabPublicInterface* s_prefabPublicInterface; + static PrefabSystemComponentInterface* s_prefabSystemComponentInterface; + }; + } +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp index 5f7f487381..d57d16fd74 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.cpp @@ -52,7 +52,6 @@ AZ_POP_DISABLE_WARNING #include #include #include -#include #include #include @@ -1206,7 +1205,7 @@ namespace AzToolsFramework SharedThumbnailKey thumbnailKey = MAKE_TKEY(AzToolsFramework::AssetBrowser::ProductThumbnailKey, assetID); if (m_showThumbnail) { - m_thumbnail->SetThumbnailKey(thumbnailKey, Thumbnailer::ThumbnailContext::DefaultContext); + m_thumbnail->SetThumbnailKey(thumbnailKey); } return; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp index ec817f4ae1..683a7b8005 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.cpp @@ -65,11 +65,11 @@ namespace AzToolsFramework UpdateVisibility(); } - void ThumbnailPropertyCtrl::SetThumbnailKey(Thumbnailer::SharedThumbnailKey key, const char* contextName) + void ThumbnailPropertyCtrl::SetThumbnailKey(Thumbnailer::SharedThumbnailKey key) { m_key = key; - m_thumbnail->SetThumbnailKey(m_key, contextName); - m_thumbnailEnlarged->SetThumbnailKey(m_key, contextName); + m_thumbnail->SetThumbnailKey(m_key); + m_thumbnailEnlarged->SetThumbnailKey(m_key); UpdateVisibility(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h index ffd8234190..be317b8a92 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ThumbnailPropertyCtrl.h @@ -34,7 +34,7 @@ namespace AzToolsFramework explicit ThumbnailPropertyCtrl(QWidget* parent = nullptr); //! Call this to set what thumbnail widget will display - void SetThumbnailKey(Thumbnailer::SharedThumbnailKey key, const char* contextName = "Default"); + void SetThumbnailKey(Thumbnailer::SharedThumbnailKey key); //! Remove current thumbnail void ClearThumbnail(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/UICore/WidgetHelpers.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/UICore/WidgetHelpers.h index 0c326f9640..379a19739e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/UICore/WidgetHelpers.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/UICore/WidgetHelpers.h @@ -11,11 +11,12 @@ #include #include +#include namespace AzToolsFramework { - /// Gets the currently active window, or the Editor's main window if there is no active window. - /// Can be used to guarantee that modal dialogs have a valid parent in every context. + //! Gets the currently active window, or the Editor's main window if there is no active window. + //! Can be used to guarantee that modal dialogs have a valid parent in every context. inline QWidget* GetActiveWindow() { QWidget* activeWindow = QApplication::activeWindow(); @@ -26,5 +27,12 @@ namespace AzToolsFramework return activeWindow; } + + //! Create a simple modal dialog with a warning message. + inline void WarningDialog(AZStd::string_view title, AZStd::string_view message) + { + QMessageBox::warning(GetActiveWindow(), QString(title.data()), QString(message.data()), QMessageBox::Ok, QMessageBox::Ok); + } + } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 5c5cdcb4b4..c1037dd1ba 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -1360,6 +1360,19 @@ namespace AzToolsFramework EndRecordManipulatorCommand(); }); + translationManipulators->InstallSurfaceManipulatorEntityIdsToIgnoreFn( + [this](const ViewportInteraction::MouseInteraction& interaction) + { + if (interaction.m_keyboardModifiers.Ctrl()) + { + return AZStd::unordered_set(); + } + else + { + return m_selectedEntityIds; + } + }); + // transfer ownership m_entityIdManipulators.m_manipulators = AZStd::move(translationManipulators); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h index 0465a7920f..8d3cef890a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -36,8 +37,6 @@ namespace AzToolsFramework { class EditorVisibleEntityDataCacheInterface; - using EntityIdSet = AZStd::unordered_set; //!< Alias for unordered_set of EntityIds. - //! Entity related data required by manipulators during action. struct EntityIdManipulatorLookup { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 53e9a97b1d..e5e09f7cb3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -86,8 +86,6 @@ set(FILES Thumbnails/Thumbnail.cpp Thumbnails/Thumbnail.h Thumbnails/Thumbnail.inl - Thumbnails/ThumbnailContext.cpp - Thumbnails/ThumbnailContext.h Thumbnails/ThumbnailerBus.h Thumbnails/ThumbnailWidget.cpp Thumbnails/ThumbnailWidget.h @@ -155,6 +153,7 @@ set(FILES Entity/SliceEditorEntityOwnershipService.h Entity/SliceEditorEntityOwnershipService.cpp Entity/SliceEditorEntityOwnershipServiceBus.h + Entity/EntityTypes.h Entity/EntityUtilityComponent.h Entity/EntityUtilityComponent.cpp Entity/ReadOnly/ReadOnlyEntityInterface.h @@ -760,6 +759,8 @@ set(FILES UI/Prefab/PrefabIntegrationManager.h UI/Prefab/PrefabIntegrationManager.cpp UI/Prefab/PrefabIntegrationInterface.h + UI/Prefab/PrefabSaveLoadHandler.h + UI/Prefab/PrefabSaveLoadHandler.cpp UI/Prefab/PrefabUiHandler.h UI/Prefab/PrefabUiHandler.cpp UI/Prefab/PrefabViewportFocusPathHandler.h diff --git a/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp b/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp index 29d72a8270..724819b0c2 100644 --- a/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp @@ -3277,4 +3277,45 @@ namespace UnitTest const AZ::Transform finalEntityTransform = AzToolsFramework::GetWorldTransform(m_entityIdBox); EXPECT_THAT(finalEntityTransform.GetTranslation(), IsCloseTolerance(expectedWorldPosition, 0.01f)); } + + TEST_F( + EditorTransformComponentSelectionRenderGeometryIntersectionManipulatorFixture, SurfaceManipulatorSelfIntersectsMeshWhenCtrlIsHeld) + { + // camera (go to position format) - 47.00, -52.00, 20.00, 0.00, -60.00 + m_cameraState.m_viewportSize = AZ::Vector2(1280.0f, 720.0f); + // position camera + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateFromMatrix3x3AndTranslation( + AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(-60.0f)), AZ::Vector3(47.0f, -52.0f, 20.0f))); + // position box + AzToolsFramework::SetWorldTransform(m_entityIdBox, AZ::Transform::CreateTranslation(AZ::Vector3(50.0f, -50.0f, 20.0f))); + + // the initial starting position of the entity + const auto initialTransformWorld = AzToolsFramework::GetWorldTransform(m_entityIdBox); + // where the surface manipulator should end up (surface of the box) + const auto finalTransformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(49.5f, -49.6337357f, 19.5793953f)); + + // calculate the position in screen space of the initial position of the entity + const auto initialPositionScreen = AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState); + // calculate the position in screen space of the final position of the entity + const auto finalPositionScreen = AzFramework::WorldToScreen(finalTransformWorld.GetTranslation(), m_cameraState); + + // select the entity (this will cause the manipulators to appear in EditorTransformComponentSelection) + AzToolsFramework::SelectEntity(m_entityIdBox); + + // press and drag the mouse (starting where the surface manipulator is) + m_actionDispatcher->CameraState(m_cameraState) + ->MousePosition(initialPositionScreen) + ->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control) + ->MouseLButtonDown() + ->MousePosition(finalPositionScreen) + ->MouseLButtonUp(); + + // read back the position of the entity now + const AZ::Transform finalManipulatorTransform = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); + + // ensure final world positions match + EXPECT_THAT(finalManipulatorTransform, IsCloseTolerance(finalTransformWorld, 0.01f)); + } } // namespace UnitTest diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabCreateBenchmarks.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabCreateBenchmarks.cpp index 8cc5e5f4d2..debca753de 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabCreateBenchmarks.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabCreateBenchmarks.cpp @@ -21,7 +21,7 @@ namespace Benchmark CreateFakePaths(numInstances); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); @@ -60,7 +60,7 @@ namespace Benchmark { const unsigned int numEntities = static_cast(state.range()); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); @@ -100,7 +100,7 @@ namespace Benchmark // plus the instance receiving them CreateFakePaths(numInstancesToAdd + 1); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); @@ -150,7 +150,7 @@ namespace Benchmark // plus the root instance CreateFakePaths(numInstances + 1); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabInstantiateBenchmarks.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabInstantiateBenchmarks.cpp index f2f3cf82b0..cc1caf1a82 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabInstantiateBenchmarks.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabInstantiateBenchmarks.cpp @@ -24,7 +24,7 @@ namespace Benchmark m_pathString); TemplateId templateToInstantiateId = firstInstance->GetTemplateId(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabLoadBenchmarks.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabLoadBenchmarks.cpp index dd29654416..60e87a7a82 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabLoadBenchmarks.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabLoadBenchmarks.cpp @@ -19,7 +19,7 @@ namespace Benchmark const unsigned int numTemplates = static_cast(state.range()); CreateFakePaths(numTemplates); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabUpdateInstancesBenchmarks.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabUpdateInstancesBenchmarks.cpp index 92a30b89f2..4f50da66c8 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabUpdateInstancesBenchmarks.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/PrefabUpdateInstancesBenchmarks.cpp @@ -24,7 +24,7 @@ namespace Benchmark const auto& nestedTemplatePath = m_paths.front(); const auto& enclosingTemplatePath = m_paths.back(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); @@ -85,7 +85,7 @@ namespace Benchmark const unsigned int numInstances = maxDepth; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); @@ -137,7 +137,7 @@ namespace Benchmark const unsigned int numInstances = numRootInstances * maxDepth; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); @@ -197,7 +197,7 @@ namespace Benchmark const unsigned int numInstances = (1 << maxDepth) - 1; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/Spawnable/SpawnAllEntitiesBenchmarks.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/Spawnable/SpawnAllEntitiesBenchmarks.cpp index ba0c3a4838..a44ce6dd28 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/Spawnable/SpawnAllEntitiesBenchmarks.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/Spawnable/SpawnAllEntitiesBenchmarks.cpp @@ -22,7 +22,7 @@ namespace Benchmark SetUpSpawnableAsset(entityCountInSourcePrefab); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); m_spawnTicket = aznew AzFramework::EntitySpawnTicket(m_spawnableAsset); @@ -59,7 +59,7 @@ namespace Benchmark SetUpSpawnableAsset(entityCountInSpawnable); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); m_spawnTicket = aznew AzFramework::EntitySpawnTicket(m_spawnableAsset); @@ -94,7 +94,7 @@ namespace Benchmark SetUpSpawnableAsset(entityCountInSpawnable); auto spawner = AzFramework::SpawnableEntitiesInterface::Get(); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { state.PauseTiming(); m_spawnTicket = aznew AzFramework::EntitySpawnTicket(m_spawnableAsset); diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/SpawnableCreateBenchmarks.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/SpawnableCreateBenchmarks.cpp index d9024114a5..e625a1ba0c 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/SpawnableCreateBenchmarks.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/Benchmark/SpawnableCreateBenchmarks.cpp @@ -27,7 +27,7 @@ namespace Benchmark auto& prefabDom = m_prefabSystemComponent->FindTemplateDom(instance->GetTemplateId()); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { // Create a vector to store spawnables so that they don't get destroyed immediately after construction. AZStd::vector> spawnables; diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabAssetFixupTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabAssetFixupTests.cpp index 4f2a19faec..a0c8f7a317 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabAssetFixupTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabAssetFixupTests.cpp @@ -176,7 +176,7 @@ namespace UnitTest TEST_F(PrefabFixupTest, Test_LoadInstanceFromPrefabDom_Overload3) { Instance instance; - Instance::EntityList entityList; + AzToolsFramework::EntityList entityList; (PrefabDomUtils::LoadInstanceFromPrefabDom(instance, entityList, m_prefabDom)); CheckInstance(instance); diff --git a/Code/Framework/AzToolsFramework/Tests/ThumbnailerTests.cpp b/Code/Framework/AzToolsFramework/Tests/ThumbnailerTests.cpp index 2a7b370684..c6e4defcc1 100644 --- a/Code/Framework/AzToolsFramework/Tests/ThumbnailerTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/ThumbnailerTests.cpp @@ -71,83 +71,4 @@ namespace UnitTest AZ::Entity* m_testEntity = nullptr; }; - - TEST_F(ThumbnailerTests, ThumbnailerComponent_RegisterUnregisterContext) - { - constexpr const char* contextName1 = "Context1"; - constexpr const char* contextName2 = "Context2"; - - auto checkHasContext = [](const char* contextName) - { - bool hasContext = false; - AzToolsFramework::Thumbnailer::ThumbnailerRequestBus::BroadcastResult(hasContext, &AzToolsFramework::Thumbnailer::ThumbnailerRequests::HasContext, contextName); - return hasContext; - }; - - EXPECT_FALSE(checkHasContext(contextName1)); - EXPECT_FALSE(checkHasContext(contextName2)); - - AzToolsFramework::Thumbnailer::ThumbnailerRequestBus::Broadcast(&AzToolsFramework::Thumbnailer::ThumbnailerRequests::RegisterContext, contextName1); - - EXPECT_TRUE(checkHasContext(contextName1)); - EXPECT_FALSE(checkHasContext(contextName2)); - - AzToolsFramework::Thumbnailer::ThumbnailerRequestBus::Broadcast(&AzToolsFramework::Thumbnailer::ThumbnailerRequests::RegisterContext, contextName2); - - EXPECT_TRUE(checkHasContext(contextName1)); - EXPECT_TRUE(checkHasContext(contextName2)); - - AzToolsFramework::Thumbnailer::ThumbnailerRequestBus::Broadcast(&AzToolsFramework::Thumbnailer::ThumbnailerRequests::UnregisterContext, contextName1); - - EXPECT_FALSE(checkHasContext(contextName1)); - EXPECT_TRUE(checkHasContext(contextName2)); - - AzToolsFramework::Thumbnailer::ThumbnailerRequestBus::Broadcast(&AzToolsFramework::Thumbnailer::ThumbnailerRequests::UnregisterContext, contextName2); - - EXPECT_FALSE(checkHasContext(contextName1)); - EXPECT_FALSE(checkHasContext(contextName2)); - } - - TEST_F(ThumbnailerTests, ThumbnailerComponent_Deactivate_ClearTumbnailContexts) - { - constexpr const char* contextName1 = "Context1"; - constexpr const char* contextName2 = "Context2"; - - auto checkHasContext = [](const char* contextName) - { - bool hasContext = false; - AzToolsFramework::Thumbnailer::ThumbnailerRequestBus::BroadcastResult(hasContext, &AzToolsFramework::Thumbnailer::ThumbnailerRequests::HasContext, contextName); - return hasContext; - }; - - AzToolsFramework::Thumbnailer::ThumbnailerRequestBus::Broadcast(&AzToolsFramework::Thumbnailer::ThumbnailerRequests::RegisterContext, contextName1); - AzToolsFramework::Thumbnailer::ThumbnailerRequestBus::Broadcast(&AzToolsFramework::Thumbnailer::ThumbnailerRequests::RegisterContext, contextName2); - - EXPECT_TRUE(checkHasContext(contextName1)); - EXPECT_TRUE(checkHasContext(contextName2)); - - m_testEntity->Deactivate(); - m_testEntity->Activate(); - - EXPECT_FALSE(checkHasContext(contextName1)); - EXPECT_FALSE(checkHasContext(contextName2)); - } - - TEST_F(ThumbnailerTests, ThumbnailerComponent_RegisterContextTwice_Assert) - { - constexpr const char* contextName1 = "Context1"; - - AzToolsFramework::Thumbnailer::ThumbnailerRequestBus::Broadcast(&AzToolsFramework::Thumbnailer::ThumbnailerRequests::RegisterContext, contextName1); - - AZ_TEST_START_TRACE_SUPPRESSION; - AzToolsFramework::Thumbnailer::ThumbnailerRequestBus::Broadcast(&AzToolsFramework::Thumbnailer::ThumbnailerRequests::RegisterContext, contextName1); - AZ_TEST_STOP_TRACE_SUPPRESSION(1); - } - - TEST_F(ThumbnailerTests, ThumbnailerComponent_UnregisterUnknownContext_Assert) - { - AZ_TEST_START_TRACE_SUPPRESSION; - AzToolsFramework::Thumbnailer::ThumbnailerRequestBus::Broadcast(&AzToolsFramework::Thumbnailer::ThumbnailerRequests::UnregisterContext, "ContextDoesNotExist"); - AZ_TEST_STOP_TRACE_SUPPRESSION(1); - } } // namespace UnitTest diff --git a/Code/LauncherUnified/launcher_generator.cmake b/Code/LauncherUnified/launcher_generator.cmake index 70bcf776af..c1cc7e81a1 100644 --- a/Code/LauncherUnified/launcher_generator.cmake +++ b/Code/LauncherUnified/launcher_generator.cmake @@ -124,7 +124,16 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC # After ensuring that we correctly support DPI scaling, this should be switched to "PerMonitor" set_property(TARGET ${project_name}.GameLauncher APPEND PROPERTY VS_DPI_AWARE "OFF") if(LY_DEFAULT_PROJECT_PATH) - set_property(TARGET ${project_name}.GameLauncher APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\"") + if (TARGET ${project_name}) + get_target_property(project_game_launcher_additional_args ${project_name} GAMELAUNCHER_ADDITIONAL_VS_DEBUGGER_COMMAND_ARGUMENTS) + if (project_game_launcher_additional_args) + # Avoid pushing param-NOTFOUND into the argument in case this property wasn't found + set(additional_game_vs_debugger_args "${project_game_launcher_additional_args}") + endif() + endif() + + set_property(TARGET ${project_name}.GameLauncher APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS + "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\" ${additional_game_vs_debugger_args}") endif() # Associate the Clients Gem Variant with each projects GameLauncher @@ -173,7 +182,16 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC ) if(LY_DEFAULT_PROJECT_PATH) - set_property(TARGET ${project_name}.ServerLauncher APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\"") + if (TARGET ${project_name}) + get_target_property(project_server_launcher_additional_args ${project_name} SERVERLAUNCHER_ADDITIONAL_VS_DEBUGGER_COMMAND_ARGUMENTS) + if (project_server_launcher_additional_args) + # Avoid pushing param-NOTFOUND into the argument in case this property wasn't found + set(additional_server_vs_debugger_args "${project_server_launcher_additional_args}") + endif() + endif() + + set_property(TARGET ${project_name}.ServerLauncher APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS + "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\" ${additional_server_vs_debugger_args}") endif() # Associate the Servers Gem Variant with each projects ServerLauncher diff --git a/Code/Legacy/CrySystem/DebugCallStack.cpp b/Code/Legacy/CrySystem/DebugCallStack.cpp index 201582b949..440525d7c8 100644 --- a/Code/Legacy/CrySystem/DebugCallStack.cpp +++ b/Code/Legacy/CrySystem/DebugCallStack.cpp @@ -47,8 +47,6 @@ extern HMODULE gDLLHandle; static HWND hwndException = 0; static bool g_bUserDialog = true; // true=on crash show dialog box, false=supress user interaction -static int PrintException(EXCEPTION_POINTERS* pex); - static bool IsFloatingPointException(EXCEPTION_POINTERS* pex); extern LONG WINAPI CryEngineExceptionFilterWER(struct _EXCEPTION_POINTERS* pExceptionPointers); @@ -667,8 +665,6 @@ INT_PTR CALLBACK DebugCallStack::ExceptionDialogProc(HWND hwndDlg, UINT message, { static EXCEPTION_POINTERS* pex; - static char errorString[32768] = ""; - switch (message) { case WM_INITDIALOG: diff --git a/Code/Legacy/CrySystem/LocalizedStringManager.cpp b/Code/Legacy/CrySystem/LocalizedStringManager.cpp index c48baa95a7..b69fc539f9 100644 --- a/Code/Legacy/CrySystem/LocalizedStringManager.cpp +++ b/Code/Legacy/CrySystem/LocalizedStringManager.cpp @@ -2620,26 +2620,7 @@ namespace UnixTimeToFileTime(unixtime, &filetime); FileTimeToSystemTime(&filetime, systemtime); } - - time_t UnixTimeFromFileTime(const FILETIME* filetime) - { - LONGLONG longlong = filetime->dwHighDateTime; - longlong <<= 32; - longlong |= filetime->dwLowDateTime; - longlong -= 116444736000000000; - return longlong / 10000000; - } - - time_t UnixTimeFromSystemTime(const SYSTEMTIME* systemtime) - { - // convert systemtime to filetime - FILETIME filetime; - SystemTimeToFileTime(systemtime, &filetime); - // convert filetime to unixtime - time_t unixtime = UnixTimeFromFileTime(&filetime); - return unixtime; - } -}; +} void CLocalizedStringsManager::LocalizeTime(time_t t, bool bMakeLocalTime, bool bShowSeconds, AZStd::string& outTimeString) { diff --git a/Code/Tools/AssetProcessor/assetprocessor_test_files.cmake b/Code/Tools/AssetProcessor/assetprocessor_test_files.cmake index bc7b16cc79..20bfc4221c 100644 --- a/Code/Tools/AssetProcessor/assetprocessor_test_files.cmake +++ b/Code/Tools/AssetProcessor/assetprocessor_test_files.cmake @@ -32,6 +32,12 @@ set(FILES native/tests/assetmanager/AssetProcessorManagerTest.h native/tests/assetmanager/ModtimeScanningTests.cpp native/tests/assetmanager/ModtimeScanningTests.h + native/tests/assetmanager/MockAssetProcessorManager.cpp + native/tests/assetmanager/MockAssetProcessorManager.h + native/tests/assetmanager/MockFileProcessor.h + native/tests/assetmanager/MockFileProcessor.cpp + native/tests/assetmanager/TestEventSignal.cpp + native/tests/assetmanager/TestEventSignal.h native/tests/utilities/assetUtilsTest.cpp native/tests/platformconfiguration/platformconfigurationtests.cpp native/tests/platformconfiguration/platformconfigurationtests.h @@ -51,6 +57,8 @@ set(FILES native/tests/SourceFileRelocatorTests.cpp native/tests/PathDependencyManagerTests.cpp native/tests/AssetProcessorMessagesTests.cpp + native/tests/ApplicationManagerTests.cpp + native/tests/ApplicationManagerTests.h native/unittests/AssetProcessingStateDataUnitTests.cpp native/unittests/AssetProcessingStateDataUnitTests.h native/unittests/AssetProcessorManagerUnitTests.cpp diff --git a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h index eb40b476b1..a4035b3951 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h +++ b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h @@ -205,7 +205,7 @@ namespace AssetProcessor void EmitResolvedDependency(const AZ::Data::AssetId& assetId, const AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry& entry); //! Internal structure that will hold all the necessary information to process jobs later. - //! We need to hold these jobs because they have declared either source dependency on other sources + //! We need to hold these jobs because they have declared either source dependency on other sources //! or a job dependency and we can only resolve these dependencies once all the create jobs are completed. struct JobToProcessEntry { @@ -229,14 +229,14 @@ namespace AssetProcessor //! Emit whenever a new asset is found or an existing asset is updated void AssetMessage(AzFramework::AssetSystem::AssetNotificationMessage message); - + // InputAssetProcessed - uses absolute asset path of input file void InputAssetProcessed(QString fullAssetPath, QString platform); void RequestInputAssetStatus(QString inputAssetPath, QString platform, QString jobDescription); void RequestPriorityAssetCompile(QString inputAssetPath, QString platform, QString jobDescription); - //! AssetProcessorManagerIdleState is emitted when APM idle state changes, we emit true when + //! AssetProcessorManagerIdleState is emitted when APM idle state changes, we emit true when //! APM is waiting for outside stimulus i.e its has eaten through all of its queues and is only waiting for //! responses back from other systems (like its waiting for responses back from the compiler) void AssetProcessorManagerIdleState(bool state); @@ -272,12 +272,12 @@ namespace AssetProcessor void AssessFilesFromScanner(QSet filePaths); - void AssessModifiedFile(QString filePath); - void AssessAddedFile(QString filePath); - void AssessDeletedFile(QString filePath); + virtual void AssessModifiedFile(QString filePath); + virtual void AssessAddedFile(QString filePath); + virtual void AssessDeletedFile(QString filePath); void OnAssetScannerStatusChange(AssetProcessor::AssetScanningStatus status); void OnJobStatusChanged(JobEntry jobEntry, JobStatus status); - + void CheckAssetProcessorIdleState(); void QuitRequested(); @@ -290,7 +290,7 @@ namespace AssetProcessor //! A network request came in asking for asset database location GetAbsoluteAssetDatabaseLocationResponse ProcessGetAbsoluteAssetDatabaseLocationRequest(MessageData messageData); - + //! This request comes in and is expected to do whatever heuristic is required in order to determine if an asset actually exists in the database. void OnRequestAssetExists(NetworkRequestID requestId, QString platform, QString searchTerm, AZ::Data::AssetId assetId); @@ -329,7 +329,7 @@ namespace AssetProcessor bool InitializeCacheRoot(); void PopulateJobStateCache(); void AutoFailJob(const AZStd::string& consoleMsg, const AZStd::string& autoFailReason, const AZStd::vector::iterator& assetIter); - + using ProductInfoList = AZStd::vector>; void WriteProductTableInfo(AZStd::pair& pair, AZStd::vector& subIds, AZStd::unordered_set& dependencyContainer, const AZStd::string& platform); @@ -362,7 +362,7 @@ namespace AssetProcessor //! Search the database and the the source dependency maps for the the sourceUuid. if found returns the cached info bool SearchSourceInfoBySourceUUID(const AZ::Uuid& sourceUuid, AssetProcessorManager::SourceInfo& result); - + //! Adds the source to the database and returns the corresponding sourceDatabase Entry void AddSourceToDatabase(AzToolsFramework::AssetDatabase::SourceDatabaseEntry& sourceDatabaseEntry, const ScanFolderInfo* scanFolder, QString relativeSourceFilePath); @@ -378,8 +378,8 @@ namespace AssetProcessor // Load the old scan folders and match them up with new scan folders. Make sure they're bool MigrateScanFolders(); - //! Checks whether the AP is aware of any source file that has indicated the inputted - //! source file as its dependency, and if found do we need to put that file back in the asset pipeline queue again + //! Checks whether the AP is aware of any source file that has indicated the inputted + //! source file as its dependency, and if found do we need to put that file back in the asset pipeline queue again QStringList GetSourceFilesWhichDependOnSourceFile(const QString& sourcePath); /** Given a BuilderSDK SourceFileDependency, try to find out what actual database source name is. @@ -399,9 +399,9 @@ namespace AssetProcessor void UpdateWildcardDependencies(JobDetails& job, size_t jobDependencySlot, QStringList& resolvedDependencyList); //! Check whether the job can be analyzed by APM, - //! A job cannot be analyzed if any of its dependent job hasn't been fingerprinted + //! A job cannot be analyzed if any of its dependent job hasn't been fingerprinted bool CanAnalyzeJob(const JobDetails& jobDetails); - + //! Analyzes and forward the job to the RCController if the job requires processing void ProcessJob(JobDetails& jobDetails); @@ -417,7 +417,7 @@ namespace AssetProcessor ThreadController* m_assetCatalog; typedef QHash FileExamineContainer; FileExamineContainer m_filesToExamine; // order does not actually matter in this (yet) - + // this map contains a list of source files that were discovered in the database before asset scanning began. // (so files from a previous run). // as asset scanning encounters files, it will remove them from this map, and when its done, @@ -455,20 +455,20 @@ namespace AssetProcessor AZ::s64 m_highestJobRunKeySoFar = 0; AZStd::vector m_jobEntries; AZStd::unordered_set m_jobsToProcess; - //! This map is required to prevent multiple sourceFile modified events been send by the APM + //! This map is required to prevent multiple sourceFile modified events been send by the APM AZStd::unordered_map m_sourceFileModTimeMap; - AZStd::unordered_map m_jobFingerprintMap; + AZStd::unordered_map m_jobFingerprintMap; AZStd::unordered_map> m_jobDescToBuilderUuidMap; - + AZStd::unique_ptr m_pathDependencyManager; AZStd::unique_ptr m_sourceFileRelocator; JobDiagnosticTracker m_jobDiagnosticTracker{}; - - QSet m_checkFoldersToRemove; //!< List of folders that needs to be checked for removal later by AP + + QSet m_checkFoldersToRemove; //!< List of folders that needs to be checked for removal later by AP //! List of all scanfolders that are present in the database but not currently watched by AP AZStd::unordered_map m_scanFoldersInDatabase; - + int m_numOfJobsToAnalyze = 0; bool m_alreadyQueuedCheckForIdle = false; @@ -502,7 +502,7 @@ namespace AssetProcessor * full absolute paths. */ void QueryAbsolutePathDependenciesRecursive(QString inputDatabasePath, SourceFilesForFingerprintingContainer& finalDependencyList, AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::TypeOfDependency dependencyType, bool reverseQuery); - + // we can't write a job to the database as not needing analysis the next time around, // until all jobs related to it are finished. This is becuase the jobs themselves are not written to the database // so until all jobs are finished, we need to re-analyze the source file next time. @@ -529,7 +529,7 @@ namespace AssetProcessor JobStarted, JobFinished, }; - + // ideally you would already have the absolute path to the file, and call this function with it: void UpdateAnalysisTrackerForFile(const char* fullPathToFile, AnalysisTrackerUpdateType updateType); diff --git a/Code/Tools/AssetProcessor/native/FileProcessor/FileProcessor.h b/Code/Tools/AssetProcessor/native/FileProcessor/FileProcessor.h index e455fe91cf..f09d424a47 100644 --- a/Code/Tools/AssetProcessor/native/FileProcessor/FileProcessor.h +++ b/Code/Tools/AssetProcessor/native/FileProcessor/FileProcessor.h @@ -19,7 +19,7 @@ namespace AzToolsFramework { - namespace AssetDatabase + namespace AssetDatabase { class FileDatabaseEntry; } @@ -40,17 +40,17 @@ namespace AssetProcessor public Q_SLOTS: //! AssetScanner changed its status void OnAssetScannerStatusChange(AssetScanningStatus status); - + //! AssetScanner found a file void AssessFilesFromScanner(QSet files); - + //! AssetScanner found a folder void AssessFoldersFromScanner(QSet folders); //! FileWatcher detected added file - void AssessAddedFile(QString fileName); + virtual void AssessAddedFile(QString fileName); //! FileWatcher detected removed file - void AssessDeletedFile(QString fileName); + virtual void AssessDeletedFile(QString fileName); //! Synchronize AssetScanner data with Files table void Sync(); diff --git a/Code/Tools/AssetProcessor/native/FileWatcher/FileWatcher.cpp b/Code/Tools/AssetProcessor/native/FileWatcher/FileWatcher.cpp index 993e7f2760..2eb56487d9 100644 --- a/Code/Tools/AssetProcessor/native/FileWatcher/FileWatcher.cpp +++ b/Code/Tools/AssetProcessor/native/FileWatcher/FileWatcher.cpp @@ -148,7 +148,6 @@ void FileWatcher::StopWatching() { if (!m_startedWatching) { - AZ_Warning("FileWatcher", false, "StopWatching() called when is not watching for file changes."); return; } diff --git a/Code/Tools/AssetProcessor/native/tests/ApplicationManagerTests.cpp b/Code/Tools/AssetProcessor/native/tests/ApplicationManagerTests.cpp new file mode 100644 index 0000000000..37717897ef --- /dev/null +++ b/Code/Tools/AssetProcessor/native/tests/ApplicationManagerTests.cpp @@ -0,0 +1,91 @@ +/* + * 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 "ApplicationManagerTests.h" + +#include +#include +#include + +namespace UnitTests +{ + bool DatabaseLocationListener::GetAssetDatabaseLocation(AZStd::string& location) + { + location = m_databaseLocation; + return true; + } + + void ApplicationManagerTest::SetUp() + { + ScopedAllocatorSetupFixture::SetUp(); + + AZ::IO::Path tempDir(m_tempDir.GetDirectory()); + m_databaseLocationListener.m_databaseLocation = (tempDir / "test_database.sqlite").Native(); + + // We need a QCoreApplication to run the event loop + int argc = 0; + m_coreApplication = AZStd::make_unique(argc, nullptr); + + m_applicationManager = AZStd::make_unique(&argc, nullptr); + m_applicationManager->m_platformConfiguration = new AssetProcessor::PlatformConfiguration{ nullptr }; + m_applicationManager->m_fileStateCache = AZStd::make_unique(); + + m_mockAPM = AZStd::make_unique(m_applicationManager->m_platformConfiguration, nullptr); + m_applicationManager->m_assetProcessorManager = m_mockAPM.get(); + + AZStd::vector platforms; + m_applicationManager->m_platformConfiguration->EnablePlatform(AssetBuilderSDK::PlatformInfo{ "pc", { "tag" } }); + m_applicationManager->m_platformConfiguration->PopulatePlatformsForScanFolder(platforms); + m_applicationManager->m_platformConfiguration->AddScanFolder( + AssetProcessor::ScanFolderInfo{ tempDir.c_str(), "test", "test", true, true, platforms }); + + m_apmThread = AZStd::make_unique(nullptr); + m_apmThread->setObjectName("APM Thread"); + m_applicationManager->m_assetProcessorManager->moveToThread(m_apmThread.get()); + m_apmThread->start(); + + m_fileProcessorThread = AZStd::make_unique(nullptr); + m_fileProcessorThread->setObjectName("File Processor Thread"); + auto fileProcessor = AZStd::make_unique(m_applicationManager->m_platformConfiguration); + fileProcessor->moveToThread(m_fileProcessorThread.get()); + m_mockFileProcessor = fileProcessor.get(); + m_applicationManager->m_fileProcessor = AZStd::move(fileProcessor); // The manager is taking ownership + m_fileProcessorThread->start(); + + auto fileWatcher = AZStd::make_unique(); + m_fileWatcher = fileWatcher.get(); + + // This is what we're testing, it will set up connections between the fileWatcher and the 2 QObject handlers we'll check + m_applicationManager->InitFileMonitor(AZStd::move(fileWatcher)); // The manager is going to take ownership of the file watcher + } + + void ApplicationManagerTest::TearDown() + { + m_apmThread->exit(); + m_fileProcessorThread->exit(); + m_mockAPM = nullptr; + + ScopedAllocatorSetupFixture::TearDown(); + } + + TEST_F(ApplicationManagerTest, FileWatcherEventsTriggered_ProperlySignalledOnCorrectThread) + { + AZ::IO::Path tempDir(m_tempDir.GetDirectory()); + + Q_EMIT m_fileWatcher->fileAdded((tempDir / "test").c_str()); + Q_EMIT m_fileWatcher->fileModified((tempDir / "test2").c_str()); + Q_EMIT m_fileWatcher->fileRemoved((tempDir / "test3").c_str()); + + EXPECT_TRUE(m_mockAPM->m_events[Added].WaitAndCheck()) << "APM Added event failed"; + EXPECT_TRUE(m_mockAPM->m_events[Modified].WaitAndCheck()) << "APM Modified event failed"; + EXPECT_TRUE(m_mockAPM->m_events[Deleted].WaitAndCheck()) << "APM Deleted event failed"; + + EXPECT_TRUE(m_mockFileProcessor->m_events[Added].WaitAndCheck()) << "File Processor Added event failed"; + EXPECT_TRUE(m_mockFileProcessor->m_events[Deleted].WaitAndCheck()) << "File Processor Deleted event failed"; + } +} diff --git a/Code/Tools/AssetProcessor/native/tests/ApplicationManagerTests.h b/Code/Tools/AssetProcessor/native/tests/ApplicationManagerTests.h new file mode 100644 index 0000000000..fea10b224b --- /dev/null +++ b/Code/Tools/AssetProcessor/native/tests/ApplicationManagerTests.h @@ -0,0 +1,64 @@ +/* + * 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 "assetmanager/MockAssetProcessorManager.h" +#include "assetmanager/MockFileProcessor.h" + +namespace UnitTests +{ + struct MockBatchApplicationManager : BatchApplicationManager + { + using ApplicationManagerBase::InitFileMonitor; + using ApplicationManagerBase::m_assetProcessorManager; + using ApplicationManagerBase::m_fileProcessor; + using ApplicationManagerBase::m_fileStateCache; + using ApplicationManagerBase::m_platformConfiguration; + using BatchApplicationManager::BatchApplicationManager; + }; + + class DatabaseLocationListener : public AzToolsFramework::AssetDatabase::AssetDatabaseRequests::Bus::Handler + { + public: + DatabaseLocationListener() + { + BusConnect(); + } + ~DatabaseLocationListener() override + { + BusDisconnect(); + } + + bool GetAssetDatabaseLocation(AZStd::string& location) override; + + AZStd::string m_databaseLocation; + }; + + struct ApplicationManagerTest : ::UnitTest::ScopedAllocatorSetupFixture + { + protected: + void SetUp() override; + void TearDown() override; + + AZ::Test::ScopedAutoTempDirectory m_tempDir; + DatabaseLocationListener m_databaseLocationListener; + + AZStd::unique_ptr m_coreApplication; + AZStd::unique_ptr m_applicationManager; + AZStd::unique_ptr m_apmThread; + AZStd::unique_ptr m_fileProcessorThread; + AZStd::unique_ptr m_mockAPM; + + // These are just aliases, no need to manage/delete them + FileWatcher* m_fileWatcher{}; + MockFileProcessor* m_mockFileProcessor{}; + }; +} diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp index 6a0da96151..c6105142f1 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp @@ -126,18 +126,18 @@ namespace AssetProcessorMessagesTests m_batchApplicationManager->InitAssetProcessorManager(); m_assetCatalog = AZStd::make_unique(nullptr, m_batchApplicationManager->m_platformConfiguration); - + m_batchApplicationManager->m_assetCatalog = m_assetCatalog.get(); m_batchApplicationManager->InitRCController(); m_batchApplicationManager->InitFileStateCache(); - m_batchApplicationManager->InitFileMonitor(); + m_batchApplicationManager->InitFileMonitor(AZStd::make_unique()); m_batchApplicationManager->InitApplicationServer(); m_batchApplicationManager->InitConnectionManager(); // Note this must be constructed after InitConnectionManager is called since it will interact with the connection manager m_assetRequestHandler = new MockAssetRequestHandler(); m_batchApplicationManager->InitAssetRequestHandler(m_assetRequestHandler); - m_batchApplicationManager->m_fileWatcher.StartWatching(); + m_batchApplicationManager->m_fileWatcher->StartWatching(); QObject::connect(m_batchApplicationManager->m_connectionManager, &ConnectionManager::ConnectionError, [](unsigned /*connId*/, QString error) { @@ -222,7 +222,7 @@ namespace AssetProcessorMessagesTests thread.join(); } - + protected: MockAssetRequestHandler* m_assetRequestHandler{}; // Not owned, AP will delete this pointer @@ -246,7 +246,7 @@ namespace AssetProcessorMessagesTests { // Test that we can successfully send network messages and have them arrive for processing // For messages that have a response, it also verifies the response comes back - // Note that several harmless warnings will be triggered due to the messages not having any data set + // Note that several harmless warnings will be triggered due to the messages not having any data set using namespace AzFramework::AssetSystem; using namespace AzToolsFramework::AssetSystem; diff --git a/Code/Tools/AssetProcessor/native/tests/FileStateCache/FileStateCacheTests.cpp b/Code/Tools/AssetProcessor/native/tests/FileStateCache/FileStateCacheTests.cpp index c4288860c3..718f41a7b7 100644 --- a/Code/Tools/AssetProcessor/native/tests/FileStateCache/FileStateCacheTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/FileStateCache/FileStateCacheTests.cpp @@ -12,10 +12,12 @@ namespace UnitTests { + using AssetFileInfo = AssetProcessor::AssetFileInfo; + void FileStateCacheTests::SetUp() { m_temporarySourceDir = QDir(m_temporaryDir.path()); - m_fileStateCache = AZStd::make_unique(); + m_fileStateCache = AZStd::make_unique(); } void FileStateCacheTests::TearDown() @@ -26,9 +28,9 @@ namespace UnitTests void FileStateCacheTests::CheckForFile(QString path, bool shouldExist) { bool exists = false; - FileStateInfo fileInfo; + AssetProcessor::FileStateInfo fileInfo; - auto* fileStateInterface = AZ::Interface::Get(); + auto* fileStateInterface = AZ::Interface::Get(); ASSERT_NE(fileStateInterface, nullptr); exists = fileStateInterface->Exists(path); @@ -142,7 +144,7 @@ namespace UnitTests TEST_F(FileStateCacheTests, PassthroughTest) { m_fileStateCache = nullptr; // Need to release the existing one first since only one handler can exist for the ebus - m_fileStateCache = AZStd::make_unique(); + m_fileStateCache = AZStd::make_unique(); QString testPath = m_temporarySourceDir.absoluteFilePath("test.txt"); CheckForFile(testPath, false); diff --git a/Code/Tools/AssetProcessor/native/tests/FileStateCache/FileStateCacheTests.h b/Code/Tools/AssetProcessor/native/tests/FileStateCache/FileStateCacheTests.h index 78ddc6aaed..14e4763d29 100644 --- a/Code/Tools/AssetProcessor/native/tests/FileStateCache/FileStateCacheTests.h +++ b/Code/Tools/AssetProcessor/native/tests/FileStateCache/FileStateCacheTests.h @@ -16,7 +16,6 @@ namespace UnitTests { using namespace testing; using ::testing::NiceMock; - using namespace AssetProcessor; class FileStateCacheTests : public ::testing::Test { @@ -29,6 +28,6 @@ namespace UnitTests protected: QTemporaryDir m_temporaryDir; QDir m_temporarySourceDir; - AZStd::unique_ptr m_fileStateCache; + AZStd::unique_ptr m_fileStateCache; }; } diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/MockAssetProcessorManager.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/MockAssetProcessorManager.cpp new file mode 100644 index 0000000000..8b14ef0915 --- /dev/null +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/MockAssetProcessorManager.cpp @@ -0,0 +1,27 @@ +/* + * 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 UnitTests +{ + void MockAssetProcessorManager::AssessAddedFile(QString filePath) + { + m_events[TestEvents::Added].Signal(); + } + + void MockAssetProcessorManager::AssessModifiedFile(QString filePath) + { + m_events[TestEvents::Modified].Signal(); + } + + void MockAssetProcessorManager::AssessDeletedFile(QString filePath) + { + m_events[TestEvents::Deleted].Signal(); + } +} diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/MockAssetProcessorManager.h b/Code/Tools/AssetProcessor/native/tests/assetmanager/MockAssetProcessorManager.h new file mode 100644 index 0000000000..dadb62d18d --- /dev/null +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/MockAssetProcessorManager.h @@ -0,0 +1,32 @@ +/* + * 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 UnitTests +{ + struct MockAssetProcessorManager : ::AssetProcessor::AssetProcessorManager + { + Q_OBJECT + + using AssetProcessorManager::AssetProcessorManager; + + public Q_SLOTS: + void AssessAddedFile(QString filePath) override; + void AssessModifiedFile(QString filePath) override; + void AssessDeletedFile(QString filePath) override; + + public: + + TestEventPair m_events[TestEvents::NumEvents]; + }; +} diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/MockFileProcessor.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/MockFileProcessor.cpp new file mode 100644 index 0000000000..ed16a4cf7f --- /dev/null +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/MockFileProcessor.cpp @@ -0,0 +1,22 @@ +/* + * 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 "MockFileProcessor.h" + +namespace UnitTests +{ + void MockFileProcessor::AssessAddedFile(QString fileName) + { + m_events[TestEvents::Added].Signal(); + } + + void MockFileProcessor::AssessDeletedFile(QString fileName) + { + m_events[TestEvents::Deleted].Signal(); + } +} diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/MockFileProcessor.h b/Code/Tools/AssetProcessor/native/tests/assetmanager/MockFileProcessor.h new file mode 100644 index 0000000000..604b5d84cd --- /dev/null +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/MockFileProcessor.h @@ -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 + * + */ + +#pragma once + +#include +#include +#include + +namespace UnitTests +{ + struct MockFileProcessor : ::AssetProcessor::FileProcessor + { + Q_OBJECT + + using FileProcessor::FileProcessor; + + public Q_SLOTS: + void AssessAddedFile(QString fileName) override; + void AssessDeletedFile(QString fileName) override; + + public: + TestEventPair m_events[TestEvents::NumEvents]; + }; +} diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/TestEventSignal.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/TestEventSignal.cpp new file mode 100644 index 0000000000..df5c676a66 --- /dev/null +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/TestEventSignal.cpp @@ -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 + * + */ + +#include +#include +#include + +namespace UnitTests +{ + void TestEventPair::Signal() + { + bool expected = false; + ASSERT_TRUE(m_signaled.compare_exchange_strong(expected, true)); + ASSERT_EQ(m_threadId, AZStd::thread_id{}); + m_threadId = AZStd::this_thread::get_id(); + m_event.release(); + } + + bool TestEventPair::WaitAndCheck() + { + constexpr int MaxWaitTimeMilliseconds = 100; + + auto thisThreadId = AZStd::this_thread::get_id(); + bool acquireSuccess = m_event.try_acquire_for(AZStd::chrono::milliseconds(MaxWaitTimeMilliseconds)); + + EXPECT_TRUE(acquireSuccess); + EXPECT_NE(m_threadId, AZStd::thread_id{}); + EXPECT_TRUE(m_threadId != thisThreadId); + + return acquireSuccess && m_threadId != AZStd::thread_id{} && m_threadId != thisThreadId; + } +} diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/TestEventSignal.h b/Code/Tools/AssetProcessor/native/tests/assetmanager/TestEventSignal.h new file mode 100644 index 0000000000..5251be5b3f --- /dev/null +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/TestEventSignal.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 + +namespace UnitTests +{ + class TestEventPair + { + AZStd::atomic_bool m_signaled{ false }; + AZStd::thread_id m_threadId; + AZStd::binary_semaphore m_event; + + public: + void Signal(); + bool WaitAndCheck(); + }; + + enum TestEvents : int + { + Added = 0, + Modified, + Deleted, + NumEvents + }; +} diff --git a/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp b/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp index 634fdbd3bb..93cc533702 100644 --- a/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.cpp @@ -431,79 +431,114 @@ void ApplicationManagerBase::DestroyPlatformConfiguration() } } -void ApplicationManagerBase::InitFileMonitor() +void ApplicationManagerBase::InitFileMonitor(AZStd::unique_ptr fileWatcher) { + m_fileWatcher = AZStd::move(fileWatcher); + for (int folderIdx = 0; folderIdx < m_platformConfiguration->GetScanFolderCount(); ++folderIdx) { const AssetProcessor::ScanFolderInfo& info = m_platformConfiguration->GetScanFolderAt(folderIdx); - m_fileWatcher.AddFolderWatch(info.ScanPath(), info.RecurseSubFolders()); + m_fileWatcher->AddFolderWatch(info.ScanPath(), info.RecurseSubFolders()); } QDir cacheRoot; if (AssetUtilities::ComputeProjectCacheRoot(cacheRoot)) { - m_fileWatcher.AddFolderWatch(cacheRoot.absolutePath(), true); + m_fileWatcher->AddFolderWatch(cacheRoot.absolutePath(), true); } if (m_platformConfiguration->GetScanFolderCount() || !cacheRoot.path().isEmpty()) { const auto cachePath = QDir::toNativeSeparators(cacheRoot.absolutePath()); + // For the handlers below, we need to make sure to use invokeMethod on any QObjects so Qt can queue + // the callback to run on the QObject's thread if needed. The APM methods for example are not thread-safe. + const auto OnFileAdded = [this, cachePath](QString path) { const bool isCacheRoot = path.startsWith(cachePath); - if (isCacheRoot) + if (!isCacheRoot) { - m_fileStateCache->AddFile(path); - } - else - { - m_assetProcessorManager->AssessAddedFile(path); - m_fileStateCache->AddFile(path); - AZ::Interface::Get()->FileAdded(path); - m_fileProcessor->AssetProcessor::FileProcessor::AssessAddedFile(path); + [[maybe_unused]] bool result = QMetaObject::invokeMethod(m_assetProcessorManager, [this, path]() + { + m_assetProcessorManager->AssessAddedFile(path); + }, Qt::QueuedConnection); + AZ_Assert(result, "Failed to invoke m_assetProcessorManager::AssessAddedFile"); + + result = QMetaObject::invokeMethod(m_fileProcessor.get(), [this, path]() + { + m_fileProcessor->AssessAddedFile(path); + }, Qt::QueuedConnection); + AZ_Assert(result, "Failed to invoke m_fileProcessor::AssessAddedFile"); + + auto cache = AZ::Interface::Get(); + + if(cache) + { + cache->FileAdded(path); + } + else + { + AZ_Error("AssetProcessor", false, "ExcludedFolderCacheInterface not found"); + } } + + m_fileStateCache->AddFile(path); }; const auto OnFileModified = [this, cachePath](QString path) { const bool isCacheRoot = path.startsWith(cachePath); - if (isCacheRoot) - { - m_assetProcessorManager->AssessModifiedFile(path); - } - else + if (!isCacheRoot) { - m_assetProcessorManager->AssessModifiedFile(path); m_fileStateCache->UpdateFile(path); } + + [[maybe_unused]] bool result = QMetaObject::invokeMethod( + m_assetProcessorManager, + [this, path] + { + m_assetProcessorManager->AssessModifiedFile(path); + }, Qt::QueuedConnection); + + AZ_Assert(result, "Failed to invoke m_assetProcessorManager::AssessModifiedFile"); }; const auto OnFileRemoved = [this, cachePath](QString path) { + [[maybe_unused]] bool result = false; const bool isCacheRoot = path.startsWith(cachePath); - if (isCacheRoot) + if (!isCacheRoot) { - m_fileStateCache->RemoveFile(path); - m_assetProcessorManager->AssessDeletedFile(path); + result = QMetaObject::invokeMethod(m_fileProcessor.get(), [this, path]() + { + m_fileProcessor->AssessDeletedFile(path); + }, Qt::QueuedConnection); + AZ_Assert(result, "Failed to invoke m_fileProcessor::AssessDeletedFile"); } - else + + result = QMetaObject::invokeMethod(m_assetProcessorManager, [this, path]() { m_assetProcessorManager->AssessDeletedFile(path); - m_fileStateCache->RemoveFile(path); - m_fileProcessor->AssessDeletedFile(path); - } + }, Qt::QueuedConnection); + AZ_Assert(result, "Failed to invoke m_assetProcessorManager::AssessDeletedFile"); + + m_fileStateCache->RemoveFile(path); }; - connect(&m_fileWatcher, &FileWatcher::fileAdded, OnFileAdded); - connect(&m_fileWatcher, &FileWatcher::fileModified, OnFileModified); - connect(&m_fileWatcher, &FileWatcher::fileRemoved, OnFileRemoved); + connect(m_fileWatcher.get(), &FileWatcher::fileAdded, OnFileAdded); + connect(m_fileWatcher.get(), &FileWatcher::fileModified, OnFileModified); + connect(m_fileWatcher.get(), &FileWatcher::fileRemoved, OnFileRemoved); } } void ApplicationManagerBase::DestroyFileMonitor() { - m_fileWatcher.ClearFolderWatches(); + if(m_fileWatcher) + { + m_fileWatcher->ClearFolderWatches(); + m_fileWatcher = nullptr; + } } void ApplicationManagerBase::DestroyApplicationServer() @@ -1357,7 +1392,7 @@ bool ApplicationManagerBase::Activate() InitFileProcessor(); InitAssetCatalog(); - InitFileMonitor(); + InitFileMonitor(AZStd::make_unique()); InitAssetScanner(); InitAssetServerHandler(); InitRCController(); diff --git a/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.h b/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.h index 8591228375..23600963f6 100644 --- a/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.h +++ b/Code/Tools/AssetProcessor/native/utilities/ApplicationManagerBase.h @@ -134,7 +134,7 @@ protected: virtual void DestroyAssetScanner(); virtual bool InitPlatformConfiguration(); virtual void DestroyPlatformConfiguration(); - virtual void InitFileMonitor(); + virtual void InitFileMonitor(AZStd::unique_ptr fileWatcher); virtual void DestroyFileMonitor(); virtual bool InitBuilderConfiguration(); virtual void InitControlRequestHandler(); @@ -191,7 +191,7 @@ protected: bool m_sourceControlReady = false; bool m_fullIdle = false; - FileWatcher m_fileWatcher; + AZStd::unique_ptr m_fileWatcher; AssetProcessor::PlatformConfiguration* m_platformConfiguration = nullptr; AssetProcessor::AssetProcessorManager* m_assetProcessorManager = nullptr; AssetProcessor::AssetCatalog* m_assetCatalog = nullptr; diff --git a/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp b/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp index 13a6b0699a..6a918b3a89 100644 --- a/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp @@ -479,7 +479,7 @@ bool GUIApplicationManager::PostActivate() return false; } - m_fileWatcher.StartWatching(); + m_fileWatcher->StartWatching(); return true; } diff --git a/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.cpp b/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.cpp index 15c8c1b7f3..bf6ed19740 100644 --- a/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.cpp +++ b/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.cpp @@ -21,16 +21,6 @@ namespace AZ { namespace SceneData { - const AZStd::string& ScriptProcessorRule::GetScriptFilename() const - { - return m_scriptFilename; - } - - DataTypes::ScriptProcessorFallbackLogic ScriptProcessorRule::GetScriptProcessorFallbackLogic() const - { - return m_fallbackLogic; - } - void ScriptProcessorRule::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); diff --git a/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.h b/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.h index 80cb670f9f..e93bb97edb 100644 --- a/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.h +++ b/Code/Tools/SceneAPI/SceneData/Rules/ScriptProcessorRule.h @@ -28,14 +28,20 @@ namespace AZ ~ScriptProcessorRule() override = default; - const AZStd::string& GetScriptFilename() const override; + inline const AZStd::string& GetScriptFilename() const override + { + return m_scriptFilename; + } inline void SetScriptFilename(AZStd::string scriptFilename) { m_scriptFilename = AZStd::move(scriptFilename); } - DataTypes::ScriptProcessorFallbackLogic GetScriptProcessorFallbackLogic() const override; + inline DataTypes::ScriptProcessorFallbackLogic GetScriptProcessorFallbackLogic() const override + { + return m_fallbackLogic; + } static void Reflect(ReflectContext* context); diff --git a/Code/Tools/SerializeContextTools/SliceConverter.cpp b/Code/Tools/SerializeContextTools/SliceConverter.cpp index cfb1998f48..3d94a6e023 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.cpp +++ b/Code/Tools/SerializeContextTools/SliceConverter.cpp @@ -553,7 +553,7 @@ namespace AZ // Create a new unmodified prefab Instance for the nested slice instance. auto nestedInstance = AZStd::make_unique(AZStd::move(instanceAlias)); - AzToolsFramework::Prefab::Instance::EntityList newEntities; + AzToolsFramework::EntityList newEntities; if (!AzToolsFramework::Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom( *nestedInstance, newEntities, nestedTemplate->get().GetPrefabDom())) { diff --git a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp index a90f2cf4a2..428ab16f13 100644 --- a/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp +++ b/Code/Tools/TestImpactFramework/Frontend/Console/Code/Source/TestImpactConsoleTestSequenceEventHandler.cpp @@ -186,7 +186,7 @@ namespace TestImpact void TestRunCompleteCallback(const Client::TestRunBase& testRun, size_t numTestRunsCompleted, size_t totalNumTestRuns) { const auto progress = - AZStd::string::format("(%03u/%03u)", numTestRunsCompleted, totalNumTestRuns, testRun.GetTargetName().c_str()); + AZStd::string::format("(%03zu/%03zu)", numTestRunsCompleted, totalNumTestRuns); AZStd::string result; switch (testRun.GetResult()) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientSequenceReport.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientSequenceReport.h index b668bda155..5c4ac76031 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientSequenceReport.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactClientSequenceReport.h @@ -391,57 +391,57 @@ namespace TestImpact // SequenceReport overrides ... AZStd::chrono::milliseconds GetDuration() const override { - return GetDuration() + m_draftedTestRunReport.GetDuration(); + return SequenceReportBase::GetDuration() + m_draftedTestRunReport.GetDuration(); } TestSequenceResult GetResult() const override { - return CalculateMultiTestSequenceResult({ GetResult(), m_draftedTestRunReport.GetResult() }); + return CalculateMultiTestSequenceResult({ SequenceReportBase::GetResult(), m_draftedTestRunReport.GetResult() }); } size_t GetTotalNumTestRuns() const override { - return GetTotalNumTestRuns() + m_draftedTestRunReport.GetTotalNumTestRuns(); + return SequenceReportBase::GetTotalNumTestRuns() + m_draftedTestRunReport.GetTotalNumTestRuns(); } size_t GetTotalNumPassingTests() const override { - return GetTotalNumPassingTests() + m_draftedTestRunReport.GetTotalNumPassingTests(); + return SequenceReportBase::GetTotalNumPassingTests() + m_draftedTestRunReport.GetTotalNumPassingTests(); } size_t GetTotalNumFailingTests() const override { - return GetTotalNumFailingTests() + m_draftedTestRunReport.GetTotalNumFailingTests(); + return SequenceReportBase::GetTotalNumFailingTests() + m_draftedTestRunReport.GetTotalNumFailingTests(); } size_t GetTotalNumDisabledTests() const override { - return GetTotalNumDisabledTests() + m_draftedTestRunReport.GetTotalNumDisabledTests(); + return SequenceReportBase::GetTotalNumDisabledTests() + m_draftedTestRunReport.GetTotalNumDisabledTests(); } size_t GetTotalNumPassingTestRuns() const override { - return GetTotalNumPassingTestRuns() + m_draftedTestRunReport.GetNumPassingTestRuns(); + return SequenceReportBase::GetTotalNumPassingTestRuns() + m_draftedTestRunReport.GetNumPassingTestRuns(); } size_t GetTotalNumFailingTestRuns() const override { - return GetTotalNumFailingTestRuns() + m_draftedTestRunReport.GetNumFailingTestRuns(); + return SequenceReportBase::GetTotalNumFailingTestRuns() + m_draftedTestRunReport.GetNumFailingTestRuns(); } size_t GetTotalNumExecutionFailureTestRuns() const override { - return GetTotalNumExecutionFailureTestRuns() + m_draftedTestRunReport.GetNumExecutionFailureTestRuns(); + return SequenceReportBase::GetTotalNumExecutionFailureTestRuns() + m_draftedTestRunReport.GetNumExecutionFailureTestRuns(); } size_t GetTotalNumTimedOutTestRuns() const override { - return GetTotalNumTimedOutTestRuns() + m_draftedTestRunReport.GetNumTimedOutTestRuns(); + return SequenceReportBase::GetTotalNumTimedOutTestRuns() + m_draftedTestRunReport.GetNumTimedOutTestRuns(); } size_t GetTotalNumUnexecutedTestRuns() const override { - return GetTotalNumUnexecutedTestRuns() + m_draftedTestRunReport.GetNumUnexecutedTestRuns(); + return SequenceReportBase::GetTotalNumUnexecutedTestRuns() + m_draftedTestRunReport.GetNumUnexecutedTestRuns(); } private: AZStd::vector m_draftedTestRuns; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h index e784c43500..0cd01f73aa 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Include/TestImpactFramework/TestImpactRepoPath.h @@ -24,13 +24,13 @@ namespace TestImpact using value_type = AZ::IO::Path::value_type; constexpr RepoPath() = default; - constexpr RepoPath(const RepoPath&) = default; - constexpr RepoPath(RepoPath&&) noexcept = default; - constexpr RepoPath(const string_type& path) noexcept; - constexpr RepoPath(const string_view_type& path) noexcept; - constexpr RepoPath(const value_type* path) noexcept; - constexpr RepoPath(const AZ::IO::PathView& path); - constexpr RepoPath(const AZ::IO::Path& path); + RepoPath(const RepoPath&) = default; + RepoPath(RepoPath&&) noexcept = default; + RepoPath(const string_type& path) noexcept; + RepoPath(const string_view_type& path) noexcept; + RepoPath(const value_type* path) noexcept; + RepoPath(const AZ::IO::PathView& path); + RepoPath(const AZ::IO::Path& path); RepoPath& operator=(const RepoPath&) noexcept = default; RepoPath& operator=(const string_type&) noexcept; @@ -52,11 +52,11 @@ namespace TestImpact // Wrappers around the AZ::IO::Path concatenation operator friend RepoPath operator/(const RepoPath& lhs, const AZ::IO::PathView& rhs); friend RepoPath operator/(const RepoPath& lhs, AZStd::string_view rhs); - friend RepoPath operator/(const RepoPath& lhs, const typename value_type* rhs); + friend RepoPath operator/(const RepoPath& lhs, const value_type* rhs); friend RepoPath operator/(const RepoPath& lhs, const RepoPath& rhs); RepoPath& operator/=(const AZ::IO::PathView& rhs); RepoPath& operator/=(AZStd::string_view rhs); - RepoPath& operator/=(const typename value_type* rhs); + RepoPath& operator/=(const value_type* rhs); RepoPath& operator/=(const RepoPath& rhs); friend bool operator==(const RepoPath& lhs, const RepoPath& rhs) noexcept; @@ -67,27 +67,27 @@ namespace TestImpact AZ::IO::Path m_path; }; - constexpr RepoPath::RepoPath(const string_type& path) noexcept + inline RepoPath::RepoPath(const string_type& path) noexcept : m_path(AZ::IO::Path(path).MakePreferred()) { } - constexpr RepoPath::RepoPath(const string_view_type& path) noexcept + inline RepoPath::RepoPath(const string_view_type& path) noexcept : m_path(AZ::IO::Path(path).MakePreferred()) { } - constexpr RepoPath::RepoPath(const value_type* path) noexcept + inline RepoPath::RepoPath(const value_type* path) noexcept : m_path(AZ::IO::Path(path).MakePreferred()) { } - constexpr RepoPath::RepoPath(const AZ::IO::PathView& path) + inline RepoPath::RepoPath(const AZ::IO::PathView& path) : m_path(AZ::IO::Path(path).MakePreferred()) { } - constexpr RepoPath::RepoPath(const AZ::IO::Path& path) + inline RepoPath::RepoPath(const AZ::IO::Path& path) : m_path(AZ::IO::Path(path).MakePreferred()) { } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.cpp index f39cfaaf10..dfa8c9c49b 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestRunSuiteFactory.cpp @@ -69,6 +69,7 @@ namespace TestImpact const auto getDuration = [Keys](const AZ::rapidxml::xml_node<>* node) AZ_POP_DISABLE_WARNING { + AZ_UNUSED(Keys); // Clang reports a warning that capturing Keys is not necessary because it is not odr-used const AZStd::string duration = node->first_attribute(Keys[DurationKey])->value(); return AZStd::chrono::milliseconds(static_cast(AZStd::stof(duration) * 1000.f)); }; @@ -86,6 +87,7 @@ namespace TestImpact const auto getStatus = [Keys](const AZ::rapidxml::xml_node<>* node) AZ_POP_DISABLE_WARNING { + AZ_UNUSED(Keys); // Clang reports a warning that capturing Keys is not necessary because it is not odr-used const AZStd::string status = node->first_attribute(Keys[StatusKey])->value(); if (status == Keys[RunKey]) { diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Pipe.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Pipe.h index 19c11dd4da..9f55ad6074 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Pipe.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Pipe.h @@ -24,8 +24,8 @@ namespace TestImpact public: Pipe(SECURITY_ATTRIBUTES& sa, HANDLE& stdChannel); Pipe(Pipe&& other) = delete; - Pipe(Pipe& other) = delete; - Pipe& operator=(Pipe& other) = delete; + Pipe(const Pipe& other) = delete; + Pipe& operator=(const Pipe& other) = delete; Pipe& operator=(Pipe&& other) = delete; //! Releases the child end of the pipe (not needed once parent has their end). diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h index 8144615aeb..c46edbbfb7 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h @@ -58,14 +58,14 @@ namespace TestImpact //! @param payloadMapProducer The client callback to be called when all jobs have finished to transform the work produced by each job into the desired output. //! @param jobCallback The client callback to be called when each job changes state. //! @return The result of the run sequence and the jobs with their associated payloads. - AZStd::pair> Execute( + AZStd::pair> Execute( const AZStd::vector& jobs, PayloadMapProducer payloadMapProducer, StdOutputRouting stdOutRouting, StdErrorRouting stdErrRouting, AZStd::optional jobTimeout, AZStd::optional runnerTimeout, - JobCallback jobCallback); + JobCallback jobCallback); private: ProcessScheduler m_processScheduler; @@ -82,17 +82,17 @@ namespace TestImpact } template - AZStd::pair> JobRunner::Execute( + AZStd::pair> JobRunner::Execute( const AZStd::vector& jobInfos, PayloadMapProducer payloadMapProducer, StdOutputRouting stdOutRouting, StdErrorRouting stdErrRouting, AZStd::optional jobTimeout, AZStd::optional runnerTimeout, - JobCallback jobCallback) + JobCallback jobCallback) { AZStd::vector processes; - AZStd::unordered_map> metas; + AZStd::unordered_map> metas; AZStd::vector jobs; jobs.reserve(jobInfos.size()); processes.reserve(jobInfos.size()); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp index c84700065f..75f02982c0 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.cpp @@ -162,7 +162,7 @@ namespace TestImpact { } - [[nodiscard]] ProcessCallbackResult operator()(const typename JobInfo& jobInfo, const TestImpact::JobMeta& meta) + [[nodiscard]] ProcessCallbackResult operator()(const JobInfo& jobInfo, const TestImpact::JobMeta& meta) { const auto id = jobInfo.GetId().m_value; const auto& args = jobInfo.GetCommand().m_args; @@ -189,7 +189,7 @@ namespace TestImpact private: const AZStd::vector& m_testTargets; - TestEngineJobMap* m_engineJobs; + TestEngineJobMap* m_engineJobs; Policy::ExecutionFailure m_executionFailurePolicy; Policy::TestFailure m_testFailurePolicy; AZStd::optional* m_callback; @@ -239,8 +239,7 @@ namespace TestImpact const RepoPath& testRunnerBinary, const RepoPath& instrumentBinary, size_t maxConcurrentRuns) - : m_maxConcurrentRuns(maxConcurrentRuns) - , m_testJobInfoGenerator(AZStd::make_unique( + : m_testJobInfoGenerator(AZStd::make_unique( sourceDir, targetBinaryDir, cacheDir, artifactDir, testRunnerBinary, instrumentBinary)) , m_testEnumerator(AZStd::make_unique(maxConcurrentRuns)) , m_instrumentedTestRunner(AZStd::make_unique(maxConcurrentRuns)) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h index 6b097f241f..bba94e38b3 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestEngine/TestImpactTestEngine.h @@ -117,7 +117,6 @@ namespace TestImpact //! Cleans up the artifacts directory of any artifacts from previous runs. void DeleteArtifactXmls() const; - size_t m_maxConcurrentRuns = 0; AZStd::unique_ptr m_testJobInfoGenerator; AZStd::unique_ptr m_testEnumerator; AZStd::unique_ptr m_instrumentedTestRunner; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientSequenceReportSerializer.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientSequenceReportSerializer.cpp index 944fd4ac38..65a5c88883 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientSequenceReportSerializer.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/TestImpactClientSequenceReportSerializer.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include @@ -735,7 +736,11 @@ namespace TestImpact }; } - template + template, + AZStd::is_same, + AZStd::is_same + >>> PolicyStateType DeserializePolicyStateType(const rapidjson::Value& serialPolicyStateType) { if constexpr (AZStd::is_same_v) @@ -750,10 +755,6 @@ namespace TestImpact { return DeserializeImpactAnalysisSequencePolicyStateMembers(serialPolicyStateType); } - else - { - static_assert(false, "Template paramater must be a valid policy state type"); - } } template @@ -775,7 +776,7 @@ namespace TestImpact serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::MaxConcurrency]].GetUint64(), testTargetTimeout ? AZStd::optional{ testTargetTimeout } : AZStd::nullopt, globalTimeout ? AZStd::optional{ globalTimeout } : AZStd::nullopt, - DeserializePolicyStateType(serialSequenceReportBase), + DeserializePolicyStateType(serialSequenceReportBase), SuiteTypeFromString(serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::Suite]].GetString()), DeserializeTestSelection(serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::SelectedTestRuns]]), DeserializeTestRunReport(serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::SelectedTestRunReport]])); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/Gamma.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/Gamma.cpp index e00af4ab88..0e44a22af3 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/Gamma.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/Gamma.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -118,19 +119,8 @@ namespace ImageProcessingAtom float m_fMaxDiff = 0.0f; }; - - static float GammaToLinear(float x) - { - return (x <= 0.04045f) ? x / 12.92f : powf((x + 0.055f) / 1.055f, 2.4f); - } - - static float LinearToGamma(float x) - { - return (x <= 0.0031308f) ? x * 12.92f : 1.055f * powf(x, 1.0f / 2.4f) - 0.055f; - } - - static FunctionLookupTable<1024> s_lutGammaToLinear(GammaToLinear, 0.04045f, 0.00001f); - static FunctionLookupTable<1024> s_lutLinearToGamma(LinearToGamma, 0.05f, 0.00001f); + static FunctionLookupTable<1024> s_lutGammaToLinear(AZ::Color::ConvertSrgbGammaToLinear, 0.04045f, 0.00001f); + static FunctionLookupTable<1024> s_lutLinearToGamma(AZ::Color::ConvertSrgbLinearToGamma, 0.05f, 0.00001f); /////////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp index cdd63dca18..44653db0a3 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnail.cpp @@ -59,10 +59,11 @@ namespace ImageProcessingAtom void ImageThumbnail::LoadThread() { + m_state = State::Loading; AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::Event( AZ::RPI::StreamingImageAsset::RTTI_Type(), &AzToolsFramework::Thumbnailer::ThumbnailerRendererRequests::RenderThumbnail, - m_key, - ImageThumbnailSize); + m_key, ImageThumbnailSize); + // wait for response from thumbnail renderer m_renderWait.acquire(); } @@ -70,6 +71,7 @@ namespace ImageProcessingAtom void ImageThumbnail::ThumbnailRendered(const QPixmap& thumbnailImage) { m_pixmap = thumbnailImage; + m_state = State::Ready; m_renderWait.release(); } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnailSystemComponent.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnailSystemComponent.cpp index 5a41411d86..f3458196d0 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnailSystemComponent.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Thumbnail/ImageThumbnailSystemComponent.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -84,18 +83,15 @@ namespace ImageProcessingAtom { using namespace AzToolsFramework::Thumbnailer; - ThumbnailerRequestsBus::Broadcast( - &ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(Thumbnails::ImageThumbnailCache), - ThumbnailContext::DefaultContext); + ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(Thumbnails::ImageThumbnailCache)); } void ImageThumbnailSystemComponent::TeardownThumbnails() { using namespace AzToolsFramework::Thumbnailer; - ThumbnailerRequestsBus::Broadcast( - &ThumbnailerRequests::UnregisterThumbnailProvider, Thumbnails::ImageThumbnailCache::ProviderName, - ThumbnailContext::DefaultContext); + ThumbnailerRequestBus::Broadcast( + &ThumbnailerRequests::UnregisterThumbnailProvider, Thumbnails::ImageThumbnailCache::ProviderName); } void ImageThumbnailSystemComponent::OnApplicationAboutToStop() diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/AliasedHeap.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/AliasedHeap.cpp index c988c4e14e..dcd4a2574c 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/AliasedHeap.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/AliasedHeap.cpp @@ -113,7 +113,7 @@ namespace AZ if (!memoryView.IsValid()) { - RHI::ResultCode::OutOfMemory; + return RHI::ResultCode::OutOfMemory; } buffer->SetDescriptor(descriptor); diff --git a/Gems/Atom/RHI/Vulkan/Code/CMakeLists.txt b/Gems/Atom/RHI/Vulkan/Code/CMakeLists.txt index 674e5aff9e..19e8943bbe 100644 --- a/Gems/Atom/RHI/Vulkan/Code/CMakeLists.txt +++ b/Gems/Atom/RHI/Vulkan/Code/CMakeLists.txt @@ -108,6 +108,7 @@ ly_add_target( BUILD_DEPENDENCIES PUBLIC AZ::AzCore + AZ::AzTest AZ::AzFramework Gem::Atom_RHI.Reflect Gem::Atom_RHI_Vulkan.Reflect @@ -130,6 +131,7 @@ ly_add_target( PRIVATE Gem::Atom_RHI_Vulkan.Private.Static Gem::Atom_RHI.Public + ${VULKAN_VALIDATION_LAYER} ) ly_add_target( diff --git a/Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_Windows.h b/Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_Windows.h index 2b2c2b015e..de5931f693 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_Windows.h +++ b/Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_Windows.h @@ -8,7 +8,7 @@ #pragma once #include -#include +#include #include #include #include diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Windows/PAL_windows.cmake b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Windows/PAL_windows.cmake index c1a1dce4ff..154757ab41 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Windows/PAL_windows.cmake +++ b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Windows/PAL_windows.cmake @@ -7,3 +7,4 @@ # set(PAL_TRAIT_ATOM_RHI_VULKAN_SUPPORTED TRUE) +set(VULKAN_VALIDATION_LAYER 3rdParty::vulkan-validationlayers) diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.cpp index 2dce5d85f3..836bcef8cf 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.cpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace AZ { @@ -45,8 +46,21 @@ namespace AZ bool Instance::Init(const Descriptor& descriptor) { - m_descriptor = descriptor; + m_descriptor = descriptor; + if (GetValidationMode() != RHI::ValidationMode::Disabled) + { + //This env var (VK_LAYER_PATH) is used by the drivers to look for VkLayer_khronos_validation.dll + AZ::Test::SetEnv("VK_LAYER_PATH", AZ::Test::GetCurrentExecutablePath().c_str(), 1); + RawStringList validationLayers = Debug::GetValidationLayers(); + m_descriptor.m_optionalLayers.insert(m_descriptor.m_requiredLayers.end(), validationLayers.begin(), validationLayers.end()); + m_descriptor.m_optionalExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); + m_descriptor.m_optionalExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } +#if defined(AZ_VULKAN_USE_DEBUG_LABELS) + m_descriptor.m_optionalExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); +#endif + m_functionLoader = FunctionLoader::Create(); if (!m_functionLoader->Init()) { @@ -88,17 +102,6 @@ namespace AZ instanceCreateInfo.pApplicationInfo = &appInfo; StringList instanceLayerNames = GetInstanceLayerNames(); - if (GetValidationMode() != RHI::ValidationMode::Disabled) - { - RawStringList validationLayers = Debug::GetValidationLayers(); - m_descriptor.m_optionalLayers.insert(m_descriptor.m_requiredLayers.end(), validationLayers.begin(), validationLayers.end()); - m_descriptor.m_optionalExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); - m_descriptor.m_optionalExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - } -#if defined(AZ_VULKAN_USE_DEBUG_LABELS) - m_descriptor.m_optionalExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); -#endif - RawStringList optionalLayers = FilterList(m_descriptor.m_optionalLayers, instanceLayerNames); m_descriptor.m_requiredLayers.insert(m_descriptor.m_requiredLayers.end(), optionalLayers.begin(), optionalLayers.end()); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp index a92d0e5ee8..b1ae2d5698 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RPIUtils.cpp @@ -14,6 +14,8 @@ #include #include +#include +#include #include namespace AZ @@ -105,6 +107,25 @@ namespace AZ return ((value - origMin) / (origMax - origMin)) * (scaledMax - scaledMin) + scaledMin; } + // Pre-compute a lookup table for converting SRGB gamma to linear + // by specifying the AZ::u8 so we don't have to do the computation + // when retrieving pixels + using ConversionLookupTable = AZStd::array; + ConversionLookupTable CreateSrgbGammaToLinearLookupTable() + { + ConversionLookupTable lookupTable; + + for (size_t i = 0; i < lookupTable.array_size; ++i) + { + float srgbValue = i / static_cast(std::numeric_limits::max()); + lookupTable[i] = AZ::Color::ConvertSrgbGammaToLinear(srgbValue); + } + + return lookupTable; + } + + static ConversionLookupTable s_SrgbGammaToLinearLookupTable = CreateSrgbGammaToLinearLookupTable(); + float RetrieveFloatValue(const AZ::u8* mem, size_t index, AZ::RHI::Format format) { switch (format) @@ -113,12 +134,23 @@ namespace AZ case AZ::RHI::Format::A8_UNORM: case AZ::RHI::Format::R8G8_UNORM: case AZ::RHI::Format::R8G8B8A8_UNORM: + case AZ::RHI::Format::A8B8G8R8_UNORM: { return mem[index] / static_cast(std::numeric_limits::max()); } + case AZ::RHI::Format::R8_UNORM_SRGB: + case AZ::RHI::Format::R8G8_UNORM_SRGB: + case AZ::RHI::Format::R8G8B8A8_UNORM_SRGB: + case AZ::RHI::Format::A8B8G8R8_UNORM_SRGB: + { + // Use a lookup table that takes an AZ::u8 instead of a float + // for better performance + return s_SrgbGammaToLinearLookupTable[mem[index]]; + } case AZ::RHI::Format::R8_SNORM: case AZ::RHI::Format::R8G8_SNORM: case AZ::RHI::Format::R8G8B8A8_SNORM: + case AZ::RHI::Format::A8B8G8R8_SNORM: { // Scale the value from AZ::s8 min/max to -1 to 1 // We need to treat -128 and -127 the same, so that we get a symmetric @@ -452,9 +484,15 @@ namespace AZ case AZ::RHI::Format::A8_UNORM: case AZ::RHI::Format::R8G8_UNORM: case AZ::RHI::Format::R8G8B8A8_UNORM: + case AZ::RHI::Format::A8B8G8R8_UNORM: + case AZ::RHI::Format::R8_UNORM_SRGB: + case AZ::RHI::Format::R8G8_UNORM_SRGB: + case AZ::RHI::Format::R8G8B8A8_UNORM_SRGB: + case AZ::RHI::Format::A8B8G8R8_UNORM_SRGB: case AZ::RHI::Format::R8_SNORM: case AZ::RHI::Format::R8G8_SNORM: case AZ::RHI::Format::R8G8B8A8_SNORM: + case AZ::RHI::Format::A8B8G8R8_SNORM: case AZ::RHI::Format::D16_UNORM: case AZ::RHI::Format::R16_UNORM: case AZ::RHI::Format::R16G16_UNORM: diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h index 016bc5c5e2..5d2c1ce4e0 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h @@ -39,16 +39,16 @@ namespace AtomToolsFramework { public: AZ_TYPE_INFO(AtomTools::AtomToolsApplication, "{A0DF25BA-6F74-4F11-9F85-0F99278D5986}"); + AZ_DISABLE_COPY_MOVE(AtomToolsApplication); using Base = AzFramework::Application; - AtomToolsApplication(int* argc, char*** argv); + AtomToolsApplication(const char* targetName, int* argc, char*** argv); ~AtomToolsApplication(); virtual bool LaunchLocalServer(); - ////////////////////////////////////////////////////////////////////////// - // AzFramework::Application + // AzFramework::Application overrides... void CreateReflectionManager() override; void Reflect(AZ::ReflectContext* context) override; void RegisterCoreComponents() override; @@ -57,43 +57,25 @@ namespace AtomToolsFramework const char* GetCurrentConfigurationName() const override; void StartCommon(AZ::Entity* systemEntity) override; void Tick() override; - void Stop() override; + void Destroy() override; protected: - ////////////////////////////////////////////////////////////////////////// // AtomsToolMainWindowNotificationBus::Handler overrides... void OnMainWindowClosing() override; - ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// // AssetDatabaseRequestsBus::Handler overrides... bool GetAssetDatabaseLocation(AZStd::string& result) override; - ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// - // AzFramework::Application overrides... - void Destroy() override; - ////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////// // AZ::ComponentApplication overrides... void QueryApplicationType(AZ::ApplicationTypeQuery& appType) const override; - ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// // AZ::UserSettingsOwnerRequestBus::Handler overrides... void SaveSettings() override; - ////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// // EditorPythonConsoleNotificationBus::Handler overrides... void OnTraceMessage(AZStd::string_view message) override; void OnErrorMessage(AZStd::string_view message) override; void OnExceptionMessage(AZStd::string_view message) override; - //////////////////////////////////////////////////////////////////////// - - //! Executable target name generally used as a prefix for logging and other saved files - virtual AZStd::string GetBuildTargetName() const; //! List of filters for assets that need to be pre-built to run the application virtual AZStd::vector GetCriticalAssetFilters() const; @@ -121,5 +103,8 @@ namespace AtomToolsFramework AtomToolsFramework::LocalSocket m_socket; AtomToolsFramework::LocalServer m_server; + + const AZStd::string m_targetName; + const AZ::Crc32 m_toolId = {}; }; } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocument.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocument.h index 2c9497df8e..f81d3a2a03 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocument.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocument.h @@ -13,19 +13,20 @@ namespace AtomToolsFramework { - /** - * AtomToolsDocument provides an API for modifying and saving documents. - */ + //! AtomToolsDocument provides an API for modifying and saving documents. class AtomToolsDocument : public AtomToolsDocumentRequestBus::Handler , private AzToolsFramework::AssetSystemBus::Handler { public: - AZ_RTTI(AtomToolsDocument, "{8992DF74-88EC-438C-B280-6E71D4C0880B}"); + AZ_RTTI(AtomToolsDocument, "{7E6CA0C4-077C-4849-B24C-6796AF3B640B}"); AZ_CLASS_ALLOCATOR(AtomToolsDocument, AZ::SystemAllocator, 0); - AZ_DISABLE_COPY(AtomToolsDocument); + AZ_DISABLE_COPY_MOVE(AtomToolsDocument); - AtomToolsDocument(); + static void Reflect(AZ::ReflectContext* context); + + AtomToolsDocument() = default; + AtomToolsDocument(const AZ::Crc32& toolId); virtual ~AtomToolsDocument(); const AZ::Uuid& GetId() const; @@ -66,8 +67,10 @@ namespace AtomToolsFramework //! This can be overridden to restore additional data. virtual bool ReopenRestoreState(); + const AZ::Crc32 m_toolId = {}; + //! The unique id of this document, used for all bus notifications and requests. - AZ::Uuid m_id = AZ::Uuid::CreateRandom(); + const AZ::Uuid m_id = AZ::Uuid::CreateRandom(); //! The absolute path to the document source file. AZStd::string m_absolutePath; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentApplication.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentApplication.h index 7b9327337c..f29f7d0d78 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentApplication.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentApplication.h @@ -9,6 +9,7 @@ #pragma once #include +#include namespace AtomToolsFramework { @@ -16,13 +17,19 @@ namespace AtomToolsFramework : public AtomToolsApplication { public: - AZ_TYPE_INFO(AtomToolsDocumentApplication, "{F4B43677-EB95-4CBB-8B8E-9EF4247E6F0D}"); + AZ_TYPE_INFO(AtomToolsDocumentApplication, "{AC892170-D353-404A-A3D8-BB039C717295}"); + AZ_DISABLE_COPY_MOVE(AtomToolsDocumentApplication); using Base = AtomToolsApplication; - AtomToolsDocumentApplication(int* argc, char*** argv); + AtomToolsDocumentApplication(const char* targetName, int* argc, char*** argv); + protected: // AtomToolsApplication overrides... + void StartCommon(AZ::Entity* systemEntity) override; + void Destroy() override; void ProcessCommandLine(const AZ::CommandLine& commandLine) override; + + AZStd::unique_ptr m_documentSystem; }; } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h index 826d2427b1..75c129e10e 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h @@ -28,7 +28,7 @@ namespace AtomToolsFramework using Base = AtomToolsMainWindow; - AtomToolsDocumentMainWindow(QWidget* parent = 0); + AtomToolsDocumentMainWindow(const AZ::Crc32& toolId, QWidget* parent = 0); ~AtomToolsDocumentMainWindow(); protected: diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h index b9af219b9e..4fdd1f8aa9 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h @@ -18,8 +18,9 @@ namespace AtomToolsFramework : public AZ::EBusTraits { public: - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + typedef AZ::Crc32 BusIdType; //! Signal that a document was created //! @param documentId unique id of document for which the notification is sent diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentSystem.h similarity index 55% rename from Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.h rename to Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentSystem.h index 5eb531ef25..64109ba80a 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentSystem.h @@ -8,60 +8,34 @@ #pragma once -#include -#include -#include - #include #include #include - -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include -#include -AZ_POP_DISABLE_WARNING +#include +#include +#include +#include namespace AtomToolsFramework { - //! AtomToolsDocumentSystemComponent is the central component for managing documents - class AtomToolsDocumentSystemComponent - : public AZ::Component - , private AtomToolsDocumentNotificationBus::Handler - , private AtomToolsDocumentSystemRequestBus::Handler + //! AtomToolsDocumentSystem Is responsible for creation, management, and requests related to documents + class AtomToolsDocumentSystem + : public AtomToolsDocumentNotificationBus::Handler + , public AtomToolsDocumentSystemRequestBus::Handler { public: - AZ_COMPONENT(AtomToolsDocumentSystemComponent, "{343A3383-6A59-4343-851B-BF84FC6CB18E}"); - - AtomToolsDocumentSystemComponent(); - ~AtomToolsDocumentSystemComponent() = default; - AtomToolsDocumentSystemComponent(const AtomToolsDocumentSystemComponent&) = delete; - AtomToolsDocumentSystemComponent& operator=(const AtomToolsDocumentSystemComponent&) = delete; + AZ_CLASS_ALLOCATOR(AtomToolsFramework::AtomToolsDocumentSystem, AZ::SystemAllocator, 0); + AZ_RTTI(AtomToolsFramework::AtomToolsDocumentSystem, "{9D31F309-6B20-40C5-813C-F1226180E1F8}"); + AZ_DISABLE_COPY_MOVE(AtomToolsDocumentSystem); static void Reflect(AZ::ReflectContext* context); - static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); - static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); - - private: - //////////////////////////////////////////////////////////////////////// - // AZ::Component interface implementation - void Init() override; - void Activate() override; - void Deactivate() override; - //////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////// - // AtomToolsDocumentNotificationBus::Handler overrides... - void OnDocumentDependencyModified(const AZ::Uuid& documentId) override; - void OnDocumentExternallyModified(const AZ::Uuid& documentId) override; - ////////////////////////////////////////////////////////////////////////// - - void QueueReopenDocuments(); - void ReopenDocuments(); + AtomToolsDocumentSystem() = default; + AtomToolsDocumentSystem(const AZ::Crc32& toolId); + ~AtomToolsDocumentSystem(); - //////////////////////////////////////////////////////////////////////// // AtomToolsDocumentSystemRequestBus::Handler overrides... - void RegisterDocumentType(AZStd::function documentCreator) override; + void RegisterDocumentType(const AtomToolsDocumentFactoryCallback& documentCreator) override; AZ::Uuid CreateDocument() override; bool DestroyDocument(const AZ::Uuid& documentId) override; AZ::Uuid OpenDocument(AZStd::string_view sourcePath) override; @@ -74,11 +48,19 @@ namespace AtomToolsFramework bool SaveDocumentAsChild(const AZ::Uuid& documentId, AZStd::string_view targetPath) override; bool SaveAllDocuments() override; AZ::u32 GetDocumentCount() const override; - //////////////////////////////////////////////////////////////////////// + + private: + // AtomToolsDocumentNotificationBus::Handler overrides... + void OnDocumentDependencyModified(const AZ::Uuid& documentId) override; + void OnDocumentExternallyModified(const AZ::Uuid& documentId) override; + + void QueueReopenDocuments(); + void ReopenDocuments(); AZ::Uuid OpenDocumentImpl(AZStd::string_view sourcePath, bool checkIfAlreadyOpen); - AZStd::function m_documentCreator; + const AZ::Crc32 m_toolId = {}; + AtomToolsDocumentFactoryCallback m_documentCreator; AZStd::unordered_map> m_documentMap; AZStd::unordered_set m_documentIdsWithExternalChanges; AZStd::unordered_set m_documentIdsWithDependencyChanges; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h index 88b5acd3be..d59dc64034 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h @@ -14,16 +14,19 @@ namespace AtomToolsFramework { class AtomToolsDocument; - //! AtomToolsDocumentSystemRequestBus provides high level requests for menus, scripts, etc. + using AtomToolsDocumentFactoryCallback = AZStd::function; + + //! AtomToolsDocumentSystemRequestBus is an interface that provides requests for high level user interactions with a system of documents class AtomToolsDocumentSystemRequests : public AZ::EBusTraits { public: - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + typedef AZ::Crc32 BusIdType; //! Register a document factory function used to create specific document types - virtual void RegisterDocumentType(AZStd::function documentCreator) = 0; + virtual void RegisterDocumentType(const AtomToolsDocumentFactoryCallback& documentCreator) = 0; //! Create a document //! @return Uuid of new document, or null Uuid if failed diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/DollyCameraBehavior.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/DollyCameraBehavior.h similarity index 67% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/DollyCameraBehavior.h rename to Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/DollyCameraBehavior.h index 5debfc4dd1..770c0e108a 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/DollyCameraBehavior.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/DollyCameraBehavior.h @@ -5,19 +5,20 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once -#include +#include -namespace MaterialEditor +namespace AtomToolsFramework { //! Moves(zooms) camera back and forth towards the target - class DollyCameraBehavior final - : public Behavior + class DollyCameraBehavior final : public ViewportInputBehavior { public: - DollyCameraBehavior() = default; + DollyCameraBehavior(ViewportInputBehaviorControllerInterface* controller); virtual ~DollyCameraBehavior() = default; + protected: void TickInternal(float x, float y, float z) override; float GetSensitivityX() override; @@ -27,4 +28,4 @@ namespace MaterialEditor static constexpr float SensitivityX = 0; static constexpr float SensitivityY = 0.005f; }; -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/IdleBehavior.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/IdleBehavior.h similarity index 50% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/IdleBehavior.h rename to Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/IdleBehavior.h index 16543a890d..c762927336 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/IdleBehavior.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/IdleBehavior.h @@ -5,18 +5,17 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once -#include +#include -namespace MaterialEditor +namespace AtomToolsFramework { - //! No action taken - class IdleBehavior final - : public Behavior + class IdleBehavior final : public ViewportInputBehavior { public: - IdleBehavior() = default; + IdleBehavior(ViewportInputBehaviorControllerInterface* controller); virtual ~IdleBehavior() = default; }; -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MoveCameraBehavior.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/MoveCameraBehavior.h similarity index 69% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MoveCameraBehavior.h rename to Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/MoveCameraBehavior.h index ea0850ba16..011f12def2 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MoveCameraBehavior.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/MoveCameraBehavior.h @@ -5,18 +5,18 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once -#include +#include -namespace MaterialEditor +namespace AtomToolsFramework { //! Moves camera along its vertical and horizontal axis - class MoveCameraBehavior final - : public Behavior + class MoveCameraBehavior final : public ViewportInputBehavior { public: - MoveCameraBehavior() = default; + MoveCameraBehavior(ViewportInputBehaviorControllerInterface* controller); virtual ~MoveCameraBehavior() = default; void End() override; @@ -29,4 +29,4 @@ namespace MaterialEditor static constexpr float SensitivityX = 0.01f; static constexpr float SensitivityY = 0.01f; }; -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/OrbitCameraBehavior.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/OrbitCameraBehavior.h similarity index 71% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/OrbitCameraBehavior.h rename to Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/OrbitCameraBehavior.h index a312d22e73..e7e20d665d 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/OrbitCameraBehavior.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/OrbitCameraBehavior.h @@ -5,19 +5,19 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once -#include +#include -namespace MaterialEditor +namespace AtomToolsFramework { //! Rotates the camera around target position, //! this can either be model center or any position in world - class OrbitCameraBehavior final - : public Behavior + class OrbitCameraBehavior final : public ViewportInputBehavior { public: - OrbitCameraBehavior() = default; + OrbitCameraBehavior(ViewportInputBehaviorControllerInterface* controller); virtual ~OrbitCameraBehavior() = default; protected: @@ -30,7 +30,6 @@ namespace MaterialEditor static constexpr float SensitivityX = 0.005f; static constexpr float SensitivityY = 0.005f; - bool m_aligned = false; }; -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/PanCameraBehavior.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/PanCameraBehavior.h similarity index 69% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/PanCameraBehavior.h rename to Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/PanCameraBehavior.h index 93233511f0..c4351f0854 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/PanCameraBehavior.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/PanCameraBehavior.h @@ -5,20 +5,20 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once -#include +#include -namespace MaterialEditor +namespace AtomToolsFramework { //! Rotates camera around its own axis, allowing to look up/down/left/right - class PanCameraBehavior final - : public Behavior + class PanCameraBehavior final : public ViewportInputBehavior { public: - PanCameraBehavior() = default; + PanCameraBehavior(ViewportInputBehaviorControllerInterface* controller); virtual ~PanCameraBehavior() = default; - + void End() override; protected: @@ -30,4 +30,4 @@ namespace MaterialEditor static constexpr float SensitivityX = 0.005f; static constexpr float SensitivityY = 0.005f; }; -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateEnvironmentBehavior.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/RotateEnvironmentBehavior.h similarity index 66% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateEnvironmentBehavior.h rename to Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/RotateEnvironmentBehavior.h index 56c7a5190a..ef90139750 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateEnvironmentBehavior.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/RotateEnvironmentBehavior.h @@ -5,9 +5,10 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once -#include +#include namespace AZ { @@ -15,16 +16,15 @@ namespace AZ { class SkyBoxFeatureProcessorInterface; } -} +} // namespace AZ -namespace MaterialEditor +namespace AtomToolsFramework { //! Rotates lighting and skybox around vertical axis - class RotateEnvironmentBehavior final - : public Behavior + class RotateEnvironmentBehavior final : public ViewportInputBehavior { public: - RotateEnvironmentBehavior() = default; + RotateEnvironmentBehavior(ViewportInputBehaviorControllerInterface* controller); virtual ~RotateEnvironmentBehavior() = default; void Start() override; @@ -38,8 +38,8 @@ namespace MaterialEditor static constexpr float SensitivityX = 0.01f; static constexpr float SensitivityY = 0; - AZ::EntityId m_iblEntityId; - AZ::Render::SkyBoxFeatureProcessorInterface* m_skyBoxFeatureProcessorInterface = nullptr; - float m_rotation = 0; + AZ::EntityId m_environmentEntityId; + AZ::Render::SkyBoxFeatureProcessorInterface* m_skyBoxFeatureProcessor = {}; + float m_rotation = {}; }; -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateModelBehavior.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/RotateModelBehavior.h similarity index 71% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateModelBehavior.h rename to Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/RotateModelBehavior.h index e2c20ab5fd..a77b840052 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateModelBehavior.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/RotateModelBehavior.h @@ -5,18 +5,18 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once -#include +#include -namespace MaterialEditor +namespace AtomToolsFramework { //! Rotates target model in viewport - class RotateModelBehavior final - : public Behavior + class RotateModelBehavior final : public ViewportInputBehavior { public: - RotateModelBehavior() = default; + RotateModelBehavior(ViewportInputBehaviorControllerInterface* controller); virtual ~RotateModelBehavior() = default; void Start() override; @@ -33,4 +33,4 @@ namespace MaterialEditor AZ::EntityId m_targetEntityId; AZ::Vector3 m_cameraRight = AZ::Vector3::CreateAxisX(); }; -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/Behavior.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/ViewportInputBehavior.h similarity index 76% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/Behavior.h rename to Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/ViewportInputBehavior.h index 8b5ff1745e..007f72a7e2 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/Behavior.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/ViewportInputBehavior.h @@ -5,22 +5,24 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once #include -#include #include +#include -namespace MaterialEditor +namespace AtomToolsFramework { - //! Performs a single type of action for MaterialEditorViewportInputController based on input + class ViewportInputBehaviorControllerInterface; + + //! Performs a single type of action for ViewportInputBehaviorController based on input //! See derived behaviors for specific details - class Behavior - : public AZ::TickBus::Handler + class ViewportInputBehavior : public AZ::TickBus::Handler { public: - Behavior(); - virtual ~Behavior(); + ViewportInputBehavior(ViewportInputBehaviorControllerInterface* controller); + virtual ~ViewportInputBehavior(); virtual void Start(); virtual void End(); @@ -45,20 +47,21 @@ namespace MaterialEditor //! If delta transform less than this, snap instantly static constexpr float SnapInterval = 0.01f; //! delta x movement accumulated during current frame - float m_x = 0; + float m_x = {}; //! delta y movement accumulated during current frame - float m_y = 0; + float m_y = {}; //! delta scroll wheel accumulated during current frame - float m_z = 0; + float m_z = {}; //! Model radius float m_radius = 1.0f; AZ::EntityId m_cameraEntityId; AZ::Vector3 m_targetPosition = AZ::Vector3::CreateZero(); - float m_distanceToTarget = 0; + float m_distanceToTarget = {}; + ViewportInputBehaviorControllerInterface* m_controller = {}; private: // AZ::TickBus::Handler interface overrides... void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; }; -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/ViewportInputBehaviorController.h similarity index 68% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.h rename to Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/ViewportInputBehaviorController.h index a68666f06d..631a4465eb 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/ViewportInputBehaviorController.h @@ -7,36 +7,51 @@ */ #pragma once +#include +#include #include #include -#include -#include -namespace MaterialEditor +namespace AtomToolsFramework { - class Behavior; + class ViewportInputBehavior; //! Provides controls for manipulating camera, model, and environment in Material Editor - class MaterialEditorViewportInputController + class ViewportInputBehaviorController : public AzFramework::SingleViewportController - , public MaterialEditorViewportInputControllerRequestBus::Handler + , public ViewportInputBehaviorControllerInterface { public: + AZ_TYPE_INFO(ViewportInputBehaviorController, "{569A0544-7654-4DCE-8156-00A71B408374}"); + AZ_CLASS_ALLOCATOR(ViewportInputBehaviorController, AZ::SystemAllocator, 0) + AZ_DISABLE_COPY_MOVE(ViewportInputBehaviorController); - AZ_TYPE_INFO(MaterialEditorViewportInputController, "{569A0544-7654-4DCE-8156-00A71B408374}"); - AZ_CLASS_ALLOCATOR(MaterialEditorViewportInputController, AZ::SystemAllocator, 0) + using KeyMask = uint32_t; - MaterialEditorViewportInputController(); - virtual ~MaterialEditorViewportInputController(); + enum Keys + { + None = 0, + Lmb = 1 << 0, + Mmb = 1 << 1, + Rmb = 1 << 2, + Alt = 1 << 3, + Ctrl = 1 << 4, + Shift = 1 << 5 + }; + + ViewportInputBehaviorController( + const AZ::EntityId& cameraEntityId, const AZ::EntityId& targetEntityId, const AZ::EntityId& environmentEntityId); + virtual ~ViewportInputBehaviorController(); - void Init(const AZ::EntityId& cameraEntityId, const AZ::EntityId& targetEntityId, const AZ::EntityId& iblEntityId); + void AddBehavior(KeyMask mask, AZStd::shared_ptr behavior); - // MaterialEditorViewportInputControllerRequestBus::Handler interface overrides... + // ViewportInputBehaviorControllerInterface overrides... const AZ::EntityId& GetCameraEntityId() const override; const AZ::EntityId& GetTargetEntityId() const override; - const AZ::EntityId& GetIblEntityId() const override; + const AZ::EntityId& GetEnvironmentEntityId() const override; const AZ::Vector3& GetTargetPosition() const override; void SetTargetPosition(const AZ::Vector3& targetPosition) override; + void SetTargetBounds(const AZ::Aabb& targetBounds) override; float GetDistanceToTarget() const override; void GetExtents(float& distanceMin, float& distanceMax) const override; float GetRadius() const override; @@ -44,31 +59,16 @@ namespace MaterialEditor void SetFieldOfView(float value) override; bool IsCameraCentered() const override; - // AzFramework::ViewportControllerInstance interface overrides... + // AzFramework::ViewportControllerInstance overrides... bool HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) override; void UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event) override; private: - using KeyMask = uint32_t; - - enum Keys - { - None = 0, - Lmb = 1 << 0, - Mmb = 1 << 1, - Rmb = 1 << 2, - Alt = 1 << 3, - Ctrl = 1 << 4, - Shift = 1 << 5 - }; - //! Calculate min and max dist and center based on mesh size of target model void CalculateExtents(); //! Determine which behavior to set based on mouse/keyboard input void EvaluateControlBehavior(); - bool m_initialized = false; - //! Input keys currently pressed KeyMask m_keys = None; //! Input key sequence changed @@ -77,16 +77,18 @@ namespace MaterialEditor float m_timeToBehaviorSwitchMs = 0; //! Current behavior of the controller - AZStd::shared_ptr m_behavior; - AZStd::unordered_map> m_behaviorMap; + AZStd::shared_ptr m_behavior; + AZStd::unordered_map> m_behaviorMap; AZ::EntityId m_cameraEntityId; //! Target entity is looking at AZ::EntityId m_targetEntityId; //! IBL entity for rotating environment lighting - AZ::EntityId m_iblEntityId; + AZ::EntityId m_environmentEntityId; //! Target position camera is pointed towards AZ::Vector3 m_targetPosition; + //! Target bounds + AZ::Aabb m_targetBounds = AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero()); //! Center of the model observed AZ::Vector3 m_modelCenter; //! Minimum distance from camera to target @@ -106,4 +108,4 @@ namespace MaterialEditor //! e.g. pressing RMB+LMB shouldn't switch into RMB behavior (or LMB behavior) first because it's virtually impossible to press both mouse buttons on the same frame static constexpr float BehaviorSwitchDelayMs = 0.1f; }; -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputControllerBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/ViewportInputBehaviorControllerInterface.h similarity index 78% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputControllerBus.h rename to Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/ViewportInputBehaviorControllerInterface.h index dbc33bf8d4..e6e5734469 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputControllerBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/ViewportInputBehaviorControllerInterface.h @@ -8,16 +8,15 @@ #pragma once #include +#include #include -namespace MaterialEditor +namespace AtomToolsFramework { - class MaterialEditorViewportInputControllerRequests - : public AZ::EBusTraits + class ViewportInputBehaviorControllerInterface { public: - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + virtual ~ViewportInputBehaviorControllerInterface() = default; //! Get entityId of viewport camera virtual const AZ::EntityId& GetCameraEntityId() const = 0; @@ -26,7 +25,7 @@ namespace MaterialEditor virtual const AZ::EntityId& GetTargetEntityId() const = 0; //! Get entityId of scene's IBL entity - virtual const AZ::EntityId& GetIblEntityId() const = 0; + virtual const AZ::EntityId& GetEnvironmentEntityId() const = 0; //! Get actual position where the camera is facing virtual const AZ::Vector3& GetTargetPosition() const = 0; @@ -35,6 +34,10 @@ namespace MaterialEditor //! @param targetPosition world space position to point camera at virtual void SetTargetPosition(const AZ::Vector3& targetPosition) = 0; + //! Set camera target bounds + //! @param targetBounds AABB of target + virtual void SetTargetBounds(const AZ::Aabb& targetBounds) = 0; + //! Get distance between camera and its target virtual float GetDistanceToTarget() const = 0; @@ -56,6 +59,4 @@ namespace MaterialEditor //! Check if camera is looking directly at a model virtual bool IsCameraCentered() const = 0; }; - - using MaterialEditorViewportInputControllerRequestBus = AZ::EBus; -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindow.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindow.h index 86e9e2f291..558447998b 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindow.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindow.h @@ -25,7 +25,7 @@ namespace AtomToolsFramework , protected AtomToolsMainWindowRequestBus::Handler { public: - AtomToolsMainWindow(QWidget* parent = 0); + AtomToolsMainWindow(const AZ::Crc32& toolId, QWidget* parent = 0); ~AtomToolsMainWindow(); protected: @@ -48,6 +48,9 @@ namespace AtomToolsFramework virtual void SetupMetrics(); virtual void UpdateMetrics(); + virtual void UpdateWindowTitle(); + + const AZ::Crc32 m_toolId = {}; AzQtComponents::FancyDocking* m_advancedDockManager = {}; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowFactoryRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowFactoryRequestBus.h deleted file mode 100644 index c93b4ba4b9..0000000000 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowFactoryRequestBus.h +++ /dev/null @@ -1,30 +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 - -namespace AtomToolsFramework -{ - //! AtomToolsMainWindowFactoryRequestBus provides - class AtomToolsMainWindowFactoryRequests : public AZ::EBusTraits - { - public: - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; - - /// Creates and shows main window - virtual void CreateMainWindow() = 0; - - //! Destroys main window and releases all cached assets - virtual void DestroyMainWindow() = 0; - }; - using AtomToolsMainWindowFactoryRequestBus = AZ::EBus; - -} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowNotificationBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowNotificationBus.h index cadd3440d9..996396aaa2 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowNotificationBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowNotificationBus.h @@ -16,7 +16,8 @@ namespace AtomToolsFramework { public: static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + typedef AZ::Crc32 BusIdType; virtual void OnMainWindowClosing(){}; }; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowRequestBus.h index cb63ff295c..e744ac92ed 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowRequestBus.h @@ -16,12 +16,14 @@ class QWidget; namespace AtomToolsFramework { - //! AtomToolsMainWindowRequestBus provides + //! AtomToolsMainWindowRequestBus provides an interface to common main application window functions like adding docked windows, + //! resizing the viewport, and other operations class AtomToolsMainWindowRequests : public AZ::EBusTraits { public: - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + typedef AZ::Crc32 BusIdType; //! Bring main window to foreground virtual void ActivateWindow() = 0; @@ -58,6 +60,7 @@ namespace AtomToolsFramework //! Releases the viewport's render target resolution lock, allowing it to match the viewport widget again. virtual void UnlockViewportRenderTargetSize() {}; }; + using AtomToolsMainWindowRequestBus = AZ::EBus; } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index 4ba1d04b71..e35e120fbe 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -46,26 +45,16 @@ AZ_POP_DISABLE_WARNING namespace AtomToolsFramework { - AZStd::string AtomToolsApplication::GetBuildTargetName() const - { - return AZStd::string("AtomTools"); - } - - const char* AtomToolsApplication::GetCurrentConfigurationName() const - { -#if defined(_RELEASE) - return "ReleaseAtomTools"; -#elif defined(_DEBUG) - return "DebugAtomTools"; -#else - return "ProfileAtomTools"; -#endif - } - - AtomToolsApplication::AtomToolsApplication(int* argc, char*** argv) + AtomToolsApplication::AtomToolsApplication(const char* targetName, int* argc, char*** argv) : Application(argc, argv) , AzQtApplication(*argc, *argv) + , m_targetName(targetName) + , m_toolId(targetName) { + // The settings registry has been created at this point, so add the CMake target + AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization( + *AZ::SettingsRegistry::Get(), m_targetName); + // Suppress spam from the Source Control system m_traceLogger.AddWindowFilter(AzToolsFramework::SCC_WINDOW); @@ -80,18 +69,26 @@ namespace AtomToolsFramework m_styleManager.reset(new AzQtComponents::StyleManager(this)); m_styleManager->initialize(this, engineRootPath); - m_timer.setInterval(1); + const int updateIntervalWhenActive = + aznumeric_cast(GetSettingOrDefault("/O3DE/AtomToolsFramework/Application/UpdateIntervalWhenActive", 1)); + const int updateIntervalWhenNotActive = + aznumeric_cast(GetSettingOrDefault("/O3DE/AtomToolsFramework/Application/UpdateIntervalWhenNotActive", 64)); + + m_timer.setInterval(updateIntervalWhenActive); connect(&m_timer, &QTimer::timeout, this, [this]() { this->PumpSystemEventLoopUntilEmpty(); this->Tick(); }); - connect(this, &QGuiApplication::applicationStateChanged, this, [this]() + connect(this, &QGuiApplication::applicationStateChanged, this, [this, updateIntervalWhenActive, updateIntervalWhenNotActive]() { // Limit the update interval when not in focus to reduce power consumption and interference with other applications - this->m_timer.setInterval((applicationState() & Qt::ApplicationActive) ? 1 : 32); + this->m_timer.setInterval( + (applicationState() & Qt::ApplicationActive) ? updateIntervalWhenActive : updateIntervalWhenNotActive); }); + + AtomToolsMainWindowNotificationBus::Handler::BusConnect(m_toolId); } AtomToolsApplication ::~AtomToolsApplication() @@ -108,6 +105,17 @@ namespace AtomToolsFramework GetSerializeContext()->CreateEditContext(); } + const char* AtomToolsApplication::GetCurrentConfigurationName() const + { +#if defined(_RELEASE) + return "ReleaseAtomTools"; +#elif defined(_DEBUG) + return "DebugAtomTools"; +#else + return "ProfileAtomTools"; +#endif + } + void AtomToolsApplication::Reflect(AZ::ReflectContext* context) { Base::Reflect(context); @@ -181,7 +189,7 @@ namespace AtomToolsFramework Base::StartCommon(systemEntity); const bool clearLogFile = GetSettingOrDefault("/O3DE/AtomToolsFramework/Application/ClearLogOnStart", false); - m_traceLogger.OpenLogFile(GetBuildTargetName() + ".log", clearLogFile); + m_traceLogger.OpenLogFile(m_targetName + ".log", clearLogFile); ConnectToAssetProcessor(); @@ -200,9 +208,6 @@ namespace AtomToolsFramework LoadSettings(); - AtomToolsMainWindowNotificationBus::Handler::BusConnect(); - AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::CreateMainWindow); - auto editorPythonEventsInterface = AZ::Interface::Get(); if (editorPythonEventsInterface) { @@ -218,16 +223,22 @@ namespace AtomToolsFramework m_timer.start(); } - void AtomToolsApplication::OnMainWindowClosing() + void AtomToolsApplication::Tick() { - ExitMainLoop(); + TickSystem(); + Base::Tick(); + + if (WasExitMainLoopRequested()) + { + m_timer.disconnect(); + quit(); + } } void AtomToolsApplication::Destroy() { - // before modules are unloaded, destroy UI to free up any assets it cached - AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::DestroyMainWindow); m_styleManager.reset(); + UnloadSettings(); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect(); AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect(); @@ -241,6 +252,11 @@ namespace AtomToolsFramework #endif } + void AtomToolsApplication::OnMainWindowClosing() + { + ExitMainLoop(); + } + AZStd::vector AtomToolsApplication::GetCriticalAssetFilters() const { return AZStd::vector({}); @@ -255,14 +271,12 @@ namespace AtomToolsFramework // and able to negotiate a connection when running a debug build // and to negotiate a connection - const auto targetName = GetBuildTargetName(); - AzFramework::AssetSystem::ConnectionSettings connectionSettings; AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings); connectionSettings.m_connectionDirection = AzFramework::AssetSystem::ConnectionSettings::ConnectionDirection::ConnectToAssetProcessor; - connectionSettings.m_connectionIdentifier = targetName; - connectionSettings.m_loggingCallback = [targetName]([[maybe_unused]] AZStd::string_view logData) + connectionSettings.m_connectionIdentifier = m_targetName; + connectionSettings.m_loggingCallback = [targetName = m_targetName]([[maybe_unused]] AZStd::string_view logData) { AZ_UNUSED(targetName); // Prevent unused warning in release builds AZ_TracePrintf(targetName.c_str(), "%.*s", aznumeric_cast(logData.size()), logData.data()); @@ -279,7 +293,7 @@ namespace AtomToolsFramework void AtomToolsApplication::CompileCriticalAssets() { - AZ_TracePrintf(GetBuildTargetName().c_str(), "Compiling critical assets.\n"); + AZ_TracePrintf(m_targetName.c_str(), "Compiling critical assets.\n"); QStringList failedAssets; @@ -288,7 +302,7 @@ namespace AtomToolsFramework // So the asset id won't be found right after CompileAssetSync call. for (const AZStd::string& assetFilters : GetCriticalAssetFilters()) { - AZ_TracePrintf(GetBuildTargetName().c_str(), "Compiling critical asset matching: %s.\n", assetFilters.c_str()); + AZ_TracePrintf(m_targetName.c_str(), "Compiling critical asset matching: %s.\n", assetFilters.c_str()); // Wait for the asset be compiled AzFramework::AssetSystem::AssetStatus status = AzFramework::AssetSystem::AssetStatus_Unknown; @@ -335,7 +349,7 @@ namespace AtomToolsFramework AZ_Assert(context, "No serialize context"); char resolvedPath[AZ_MAX_PATH_LEN] = ""; - AZStd::string fileName = "@user@/" + GetBuildTargetName() + "UserSettings.xml"; + AZStd::string fileName = "@user@/" + m_targetName + "UserSettings.xml"; AZ::IO::FileIOBase::GetInstance()->ResolvePath( fileName.c_str(), resolvedPath, AZ_ARRAY_SIZE(resolvedPath)); @@ -350,7 +364,7 @@ namespace AtomToolsFramework AZ_Assert(context, "No serialize context"); char resolvedPath[AZ_MAX_PATH_LEN] = ""; - AZStd::string fileName = "@user@/" + GetBuildTargetName() + "UserSettings.xml"; + AZStd::string fileName = "@user@/" + m_targetName + "UserSettings.xml"; AZ::IO::FileIOBase::GetInstance()->ResolvePath(fileName.c_str(), resolvedPath, AZ_MAX_PATH_LEN); @@ -376,8 +390,7 @@ namespace AtomToolsFramework const AZStd::string activateWindowSwitchName = "activatewindow"; if (commandLine.HasSwitch(activateWindowSwitchName)) { - AtomToolsFramework::AtomToolsMainWindowRequestBus::Broadcast( - &AtomToolsFramework::AtomToolsMainWindowRequestBus::Handler::ActivateWindow); + AtomToolsMainWindowRequestBus::Event(m_toolId, &AtomToolsMainWindowRequestBus::Handler::ActivateWindow); } const AZStd::string timeoputSwitchName = "timeout"; @@ -385,14 +398,11 @@ namespace AtomToolsFramework { const AZStd::string& timeoutValue = commandLine.GetSwitchValue(timeoputSwitchName, 0); const uint32_t timeoutInMs = atoi(timeoutValue.c_str()); - AZ_Printf(GetBuildTargetName().c_str(), "Timeout scheduled, shutting down in %u ms", timeoutInMs); - QTimer::singleShot( - timeoutInMs, - [this] - { - AZ_Printf(GetBuildTargetName().c_str(), "Timeout reached, shutting down"); - ExitMainLoop(); - }); + AZ_Printf(m_targetName.c_str(), "Timeout scheduled, shutting down in %u ms", timeoutInMs); + QTimer::singleShot(timeoutInMs, [this] { + AZ_Printf(m_targetName.c_str(), "Timeout reached, shutting down"); + ExitMainLoop(); + }); } // Process command line options for running one or more python scripts on startup @@ -403,7 +413,7 @@ namespace AtomToolsFramework const AZStd::string runPythonScriptPath = commandLine.GetSwitchValue(runPythonScriptSwitchName, runPythonScriptIndex); AZStd::vector runPythonArgs; - AZ_Printf(GetBuildTargetName().c_str(), "Launching script: %s", runPythonScriptPath.c_str()); + AZ_Printf(m_targetName.c_str(), "Launching script: %s", runPythonScriptPath.c_str()); AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast( &AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs, runPythonScriptPath, runPythonArgs); } @@ -486,27 +496,6 @@ namespace AtomToolsFramework return false; } - void AtomToolsApplication::Tick() - { - TickSystem(); - Base::Tick(); - - if (WasExitMainLoopRequested()) - { - m_timer.disconnect(); - quit(); - } - } - - void AtomToolsApplication::Stop() - { - AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::DestroyMainWindow); - m_styleManager.reset(); - - UnloadSettings(); - Base::Stop(); - } - void AtomToolsApplication::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const { appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Tool; @@ -524,7 +513,7 @@ namespace AtomToolsFramework for (auto& line : lines) { - AZ_TracePrintf(GetBuildTargetName().c_str(), "Python: %s\n", line.c_str()); + AZ_TracePrintf(m_targetName.c_str(), "Python: %s\n", line.c_str()); } #endif } @@ -537,7 +526,7 @@ namespace AtomToolsFramework void AtomToolsApplication::OnExceptionMessage([[maybe_unused]] AZStd::string_view message) { - AZ_Error(GetBuildTargetName().c_str(), false, "Python: " AZ_STRING_FORMAT, AZ_STRING_ARG(message)); + AZ_Error(m_targetName.c_str(), false, "Python: " AZ_STRING_FORMAT, AZ_STRING_ARG(message)); } // Copied from PyIdleWaitFrames in CryEdit.cpp @@ -577,6 +566,8 @@ namespace AtomToolsFramework void AtomToolsApplication::PyExit() { - AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::ExitMainLoop); + QTimer::singleShot(0, []() { + AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::ExitMainLoop); + }); } } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AssetBrowser/AtomToolsAssetBrowser.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AssetBrowser/AtomToolsAssetBrowser.cpp index 8394efa46f..81bdaf380f 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AssetBrowser/AtomToolsAssetBrowser.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AssetBrowser/AtomToolsAssetBrowser.cpp @@ -89,13 +89,14 @@ namespace AtomToolsFramework void AtomToolsAssetBrowser::SelectEntries(const AZStd::string& absolutePath) { - if (!absolutePath.empty()) + AZ::TickBus::Handler::BusDisconnect(); + + m_pathToSelect = absolutePath; + if (!m_pathToSelect.empty() && AzFramework::StringFunc::Path::Normalize(m_pathToSelect)) { // Selecting a new asset in the browser is not guaranteed to happen immediately. // The asset browser model notifications are sent before the model is updated. // Instead of relying on the notifications, queue the selection and process it on tick until this change occurs. - m_pathToSelect = absolutePath; - AzFramework::StringFunc::Path::Normalize(m_pathToSelect); AZ::TickBus::Handler::BusConnect(); } } @@ -201,25 +202,29 @@ namespace AtomToolsFramework AZ_UNUSED(time); AZ_UNUSED(deltaTime); - if (!m_pathToSelect.empty()) + if (m_pathToSelect.empty()) { - // Attempt to select the new path - AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Broadcast( - &AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Events::SelectFileAtPath, m_pathToSelect); + AZ::TickBus::Handler::BusDisconnect(); + m_pathToSelect.clear(); + return; + } - // Iterate over the selected entries to verify if the selection was made - for (const AssetBrowserEntry* entry : m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets()) + // Attempt to select the new path + AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Broadcast( + &AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Events::SelectFileAtPath, m_pathToSelect); + + // Iterate over the selected entries to verify if the selection was made + for (const AssetBrowserEntry* entry : m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets()) + { + if (entry) { - if (entry) + AZStd::string sourcePath = entry->GetFullPath(); + AzFramework::StringFunc::Path::Normalize(sourcePath); + if (m_pathToSelect == sourcePath) { - AZStd::string sourcePath = entry->GetFullPath(); - AzFramework::StringFunc::Path::Normalize(sourcePath); - if (m_pathToSelect == sourcePath) - { - // Once the selection is confirmed, cancel the operation and disconnect - AZ::TickBus::Handler::BusDisconnect(); - m_pathToSelect.clear(); - } + // Once the selection is confirmed, cancel the operation and disconnect + AZ::TickBus::Handler::BusDisconnect(); + m_pathToSelect.clear(); } } } diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AssetGridDialog/AssetGridDialog.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AssetGridDialog/AssetGridDialog.cpp index ed19c4890d..806c5fddbc 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AssetGridDialog/AssetGridDialog.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AssetGridDialog/AssetGridDialog.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -105,9 +104,7 @@ namespace AtomToolsFramework AzToolsFramework::Thumbnailer::ThumbnailWidget* thumbnail = new AzToolsFramework::Thumbnailer::ThumbnailWidget(itemWidget); thumbnail->setFixedSize(m_tileSize); - thumbnail->SetThumbnailKey( - MAKE_TKEY(AzToolsFramework::AssetBrowser::ProductThumbnailKey, selectableAsset.m_assetId), - AzToolsFramework::Thumbnailer::ThumbnailContext::DefaultContext); + thumbnail->SetThumbnailKey(MAKE_TKEY(AzToolsFramework::AssetBrowser::ProductThumbnailKey, selectableAsset.m_assetId)); thumbnail->updateGeometry(); itemWidget->layout()->addWidget(thumbnail); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.cpp index 978ac52cca..a67e00a955 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -19,7 +18,6 @@ namespace AtomToolsFramework { m_descriptors.insert(m_descriptors.end(), { AtomToolsFrameworkSystemComponent::CreateDescriptor(), - AtomToolsDocumentSystemComponent::CreateDescriptor(), AtomToolsMainWindowSystemComponent::CreateDescriptor(), PerformanceMonitorSystemComponent::CreateDescriptor(), PreviewRendererSystemComponent::CreateDescriptor(), @@ -30,7 +28,6 @@ namespace AtomToolsFramework { return AZ::ComponentTypeList{ azrtti_typeid(), - azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkSystemComponent.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkSystemComponent.cpp index adba947092..840d00aa6c 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkSystemComponent.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkSystemComponent.cpp @@ -7,28 +7,32 @@ */ #include -#include #include +#include -#include +#include +#include #include +#include namespace AtomToolsFramework { void AtomToolsFrameworkSystemComponent::Reflect(AZ::ReflectContext* context) { - AtomToolsFramework::DynamicProperty::Reflect(context); - AtomToolsFramework::DynamicPropertyGroup::Reflect(context); + DynamicProperty::Reflect(context); + DynamicPropertyGroup::Reflect(context); + AtomToolsDocument::Reflect(context); + AtomToolsDocumentSystem::Reflect(context); - if (AZ::SerializeContext* serialize = azrtti_cast(context)) + if (auto serialize = azrtti_cast(context)) { serialize->Class() ->Version(0) ; - if (AZ::EditContext* ec = serialize->GetEditContext()) + if (auto editContext = serialize->GetEditContext()) { - ec->Class("AtomToolsFrameworkSystemComponent", "") + editContext->Class("AtomToolsFrameworkSystemComponent", "") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System")) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocument.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocument.cpp index 2660a1e8fb..ac94a0b399 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocument.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocument.cpp @@ -9,21 +9,59 @@ #include #include #include +#include +#include +#include #include namespace AtomToolsFramework { - AtomToolsDocument::AtomToolsDocument() + void AtomToolsDocument::Reflect(AZ::ReflectContext* context) + { + if (auto serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(0); + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("AtomToolsDocumentRequestBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "Editor") + ->Attribute(AZ::Script::Attributes::Module, "atomtools") + ->Event("GetAbsolutePath", &AtomToolsDocumentRequestBus::Events::GetAbsolutePath) + ->Event("Open", &AtomToolsDocumentRequestBus::Events::Open) + ->Event("Reopen", &AtomToolsDocumentRequestBus::Events::Reopen) + ->Event("Close", &AtomToolsDocumentRequestBus::Events::Close) + ->Event("Save", &AtomToolsDocumentRequestBus::Events::Save) + ->Event("SaveAsChild", &AtomToolsDocumentRequestBus::Events::SaveAsChild) + ->Event("SaveAsCopy", &AtomToolsDocumentRequestBus::Events::SaveAsCopy) + ->Event("IsOpen", &AtomToolsDocumentRequestBus::Events::IsOpen) + ->Event("IsModified", &AtomToolsDocumentRequestBus::Events::IsModified) + ->Event("IsSavable", &AtomToolsDocumentRequestBus::Events::IsSavable) + ->Event("CanUndo", &AtomToolsDocumentRequestBus::Events::CanUndo) + ->Event("CanRedo", &AtomToolsDocumentRequestBus::Events::CanRedo) + ->Event("Undo", &AtomToolsDocumentRequestBus::Events::Undo) + ->Event("Redo", &AtomToolsDocumentRequestBus::Events::Redo) + ->Event("BeginEdit", &AtomToolsDocumentRequestBus::Events::BeginEdit) + ->Event("EndEdit", &AtomToolsDocumentRequestBus::Events::EndEdit) + ; + } + } + + AtomToolsDocument::AtomToolsDocument(const AZ::Crc32& toolId) + : m_toolId(toolId) { AtomToolsDocumentRequestBus::Handler::BusConnect(m_id); - AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsDocumentNotificationBus::Events::OnDocumentCreated, m_id); + AtomToolsDocumentNotificationBus::Event(m_toolId, &AtomToolsDocumentNotificationBus::Events::OnDocumentCreated, m_id); } AtomToolsDocument::~AtomToolsDocument() { - AzToolsFramework::AssetSystemBus::Handler::BusDisconnect(); - AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsDocumentNotificationBus::Events::OnDocumentDestroyed, m_id); + AtomToolsDocumentNotificationBus::Event(m_toolId, &AtomToolsDocumentNotificationBus::Events::OnDocumentDestroyed, m_id); AtomToolsDocumentRequestBus::Handler::BusDisconnect(); + AzToolsFramework::AssetSystemBus::Handler::BusDisconnect(); } const AZ::Uuid& AtomToolsDocument::GetId() const @@ -80,10 +118,10 @@ namespace AtomToolsFramework return false; } - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentModified, m_id); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentModified, m_id); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id); return true; } @@ -169,8 +207,8 @@ namespace AtomToolsFramework AZ_TracePrintf("AtomToolsDocument", "Document closed: '%s'.\n", m_absolutePath.c_str()); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentClosed, m_id); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentClosed, m_id); // Clearing after notification so paths are still available Clear(); @@ -211,8 +249,8 @@ namespace AtomToolsFramework // The history index is one beyond the last executed command. Decrement the index then execute undo. m_undoHistory[--m_undoHistoryIndex].first(); AZ_TracePrintf("AtomToolsDocument", "Document undo: '%s'.\n", m_absolutePath.c_str()); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id); return true; } return false; @@ -225,8 +263,8 @@ namespace AtomToolsFramework // Execute the current redo command then move the history index to the next position. m_undoHistory[m_undoHistoryIndex++].second(); AZ_TracePrintf("AtomToolsDocument", "Document redo: '%s'.\n", m_absolutePath.c_str()); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id); return true; } return false; @@ -259,8 +297,8 @@ namespace AtomToolsFramework { AZ_TracePrintf("AtomToolsDocument", "Document opened: '%s'.\n", m_absolutePath.c_str()); AzToolsFramework::AssetSystemBus::Handler::BusConnect(); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, m_id); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, m_id); return true; } @@ -282,8 +320,8 @@ namespace AtomToolsFramework &AzToolsFramework::SourceControlCommandBus::Events::RequestEdit, m_savePathNormalized.c_str(), true, [](bool, const AzToolsFramework::SourceControlFileInfo&) {}); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentSaved, m_id); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentSaved, m_id); return true; } @@ -319,8 +357,8 @@ namespace AtomToolsFramework // Assign the index to the end of history m_undoHistoryIndex = aznumeric_cast(m_undoHistory.size()); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id); } void AtomToolsDocument::SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, [[maybe_unused]] AZ::Uuid sourceUUID) @@ -333,16 +371,16 @@ namespace AtomToolsFramework if (!m_ignoreSourceFileChangeToSelf) { AZ_TracePrintf("AtomToolsDocument", "Document changed externally: '%s'.\n", m_absolutePath.c_str()); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentExternallyModified, m_id); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentExternallyModified, m_id); } m_ignoreSourceFileChangeToSelf = false; } else if (m_sourceDependencies.find(sourcePath) != m_sourceDependencies.end()) { AZ_TracePrintf("AtomToolsDocument", "Document dependency changed: '%s'.\n", m_absolutePath.c_str()); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentDependencyModified, m_id); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentDependencyModified, m_id); } } diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentApplication.cpp index beb414bb6c..cea418d28a 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentApplication.cpp @@ -11,11 +11,23 @@ namespace AtomToolsFramework { - AtomToolsDocumentApplication::AtomToolsDocumentApplication(int* argc, char*** argv) - : Base(argc, argv) + AtomToolsDocumentApplication::AtomToolsDocumentApplication(const char* targetName, int* argc, char*** argv) + : Base(targetName, argc, argv) { } + void AtomToolsDocumentApplication::StartCommon(AZ::Entity* systemEntity) + { + Base::StartCommon(systemEntity); + m_documentSystem.reset(aznew AtomToolsDocumentSystem(m_toolId)); + } + + void AtomToolsDocumentApplication::Destroy() + { + m_documentSystem.reset(); + Base::Destroy(); + } + void AtomToolsDocumentApplication::ProcessCommandLine(const AZ::CommandLine& commandLine) { // Process command line options for opening documents on startup @@ -24,8 +36,8 @@ namespace AtomToolsFramework { const AZStd::string openDocumentPath = commandLine.GetMiscValue(openDocumentIndex); - AZ_Printf(GetBuildTargetName().c_str(), "Opening document: %s", openDocumentPath.c_str()); - AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::OpenDocument, openDocumentPath); + AZ_Printf(m_targetName.c_str(), "Opening document: %s", openDocumentPath.c_str()); + AtomToolsDocumentSystemRequestBus::Event(m_toolId, &AtomToolsDocumentSystemRequestBus::Events::OpenDocument, openDocumentPath); } Base::ProcessCommandLine(commandLine); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentMainWindow.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentMainWindow.cpp index e8be20097f..f2db527ea8 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentMainWindow.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentMainWindow.cpp @@ -25,13 +25,13 @@ AZ_POP_DISABLE_WARNING namespace AtomToolsFramework { - AtomToolsDocumentMainWindow::AtomToolsDocumentMainWindow(QWidget* parent /* = 0 */) - : AtomToolsMainWindow(parent) + AtomToolsDocumentMainWindow::AtomToolsDocumentMainWindow(const AZ::Crc32& toolId, QWidget* parent /* = 0 */) + : AtomToolsMainWindow(toolId, parent) { setObjectName("AtomToolsDocumentMainWindow"); AddDocumentMenus(); AddDocumentTabBar(); - AtomToolsDocumentNotificationBus::Handler::BusConnect(); + AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId); } AtomToolsDocumentMainWindow::~AtomToolsDocumentMainWindow() @@ -49,8 +49,8 @@ namespace AtomToolsFramework AZStd::string savePath; if (GetCreateDocumentParams(openPath, savePath)) { - AtomToolsDocumentSystemRequestBus::Broadcast( - &AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile, openPath, savePath); + AtomToolsDocumentSystemRequestBus::Event( + m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile, openPath, savePath); } }, QKeySequence::New); m_menuFile->insertAction(insertPostion, m_actionNew); @@ -59,7 +59,7 @@ namespace AtomToolsFramework AZStd::string openPath; if (GetOpenDocumentParams(openPath)) { - AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::OpenDocument, openPath); + AtomToolsDocumentSystemRequestBus::Event(m_toolId, &AtomToolsDocumentSystemRequestBus::Events::OpenDocument, openPath); } }, QKeySequence::Open); m_menuFile->insertAction(insertPostion, m_actionOpen); @@ -68,7 +68,8 @@ namespace AtomToolsFramework m_actionSave = CreateAction("&Save", [this]() { const AZ::Uuid documentId = GetDocumentTabId(m_tabWidget->currentIndex()); bool result = false; - AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsDocumentSystemRequestBus::Events::SaveDocument, documentId); + AtomToolsDocumentSystemRequestBus::EventResult( + result, m_toolId, &AtomToolsDocumentSystemRequestBus::Events::SaveDocument, documentId); if (!result) { SetStatusError(tr("Document save failed: %1").arg(GetDocumentPath(documentId))); @@ -81,8 +82,9 @@ namespace AtomToolsFramework const QString documentPath = GetDocumentPath(documentId); bool result = false; - AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsDocumentSystemRequestBus::Events::SaveDocumentAsCopy, - documentId, GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData()); + AtomToolsDocumentSystemRequestBus::EventResult( + result, m_toolId, &AtomToolsDocumentSystemRequestBus::Events::SaveDocumentAsCopy, documentId, + GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData()); if (!result) { SetStatusError(tr("Document save failed: %1").arg(GetDocumentPath(documentId))); @@ -95,8 +97,9 @@ namespace AtomToolsFramework const QString documentPath = GetDocumentPath(documentId); bool result = false; - AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsDocumentSystemRequestBus::Events::SaveDocumentAsChild, - documentId, GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData()); + AtomToolsDocumentSystemRequestBus::EventResult( + result, m_toolId, &AtomToolsDocumentSystemRequestBus::Events::SaveDocumentAsChild, documentId, + GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData()); if (!result) { SetStatusError(tr("Document save failed: %1").arg(GetDocumentPath(documentId))); @@ -106,7 +109,8 @@ namespace AtomToolsFramework m_actionSaveAll = CreateAction("Save A&ll", [this]() { bool result = false; - AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsDocumentSystemRequestBus::Events::SaveAllDocuments); + AtomToolsDocumentSystemRequestBus::EventResult( + result, m_toolId, &AtomToolsDocumentSystemRequestBus::Events::SaveAllDocuments); if (!result) { SetStatusError(tr("Document save all failed")); @@ -117,18 +121,19 @@ namespace AtomToolsFramework m_actionClose = CreateAction("&Close", [this]() { const AZ::Uuid documentId = GetDocumentTabId(m_tabWidget->currentIndex()); - AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId); + AtomToolsDocumentSystemRequestBus::Event(m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId); }, QKeySequence::Close); m_menuFile->insertAction(insertPostion, m_actionClose); - m_actionCloseAll = CreateAction("Close All", []() { - AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseAllDocuments); + m_actionCloseAll = CreateAction("Close All", [this]() { + AtomToolsDocumentSystemRequestBus::Event(m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CloseAllDocuments); }); m_menuFile->insertAction(insertPostion, m_actionCloseAll); m_actionCloseOthers = CreateAction("Close Others", [this]() { const AZ::Uuid documentId = GetDocumentTabId(m_tabWidget->currentIndex()); - AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId); + AtomToolsDocumentSystemRequestBus::Event( + m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId); }); m_menuFile->insertAction(insertPostion, m_actionCloseOthers); m_menuFile->insertSeparator(insertPostion); @@ -194,12 +199,12 @@ namespace AtomToolsFramework // This should automatically clear the active document connect(m_tabWidget, &QTabWidget::currentChanged, this, [this](int tabIndex) { const AZ::Uuid documentId = GetDocumentTabId(tabIndex); - AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId); + AtomToolsDocumentNotificationBus::Event(m_toolId,&AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId); }); connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, [this](int tabIndex) { const AZ::Uuid documentId = GetDocumentTabId(tabIndex); - AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId); + AtomToolsDocumentSystemRequestBus::Event(m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId); }); // Add context menu for right-clicking on tabs @@ -341,15 +346,18 @@ namespace AtomToolsFramework const QString selectActionName = (currentTabIndex == clickedTabIndex) ? "Select in Browser" : "Select"; tabMenu.addAction(selectActionName, [this, clickedTabIndex]() { const AZ::Uuid documentId = GetDocumentTabId(clickedTabIndex); - AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId); + AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId); }); tabMenu.addAction("Close", [this, clickedTabIndex]() { const AZ::Uuid documentId = GetDocumentTabId(clickedTabIndex); - AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId); + AtomToolsDocumentSystemRequestBus::Event( + m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId); }); auto closeOthersAction = tabMenu.addAction("Close Others", [this, clickedTabIndex]() { const AZ::Uuid documentId = GetDocumentTabId(clickedTabIndex); - AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId); + AtomToolsDocumentSystemRequestBus::Event( + m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId); }); closeOthersAction->setEnabled(tabBar->count() > 1); tabMenu.exec(QCursor::pos()); @@ -472,14 +480,14 @@ namespace AtomToolsFramework void AtomToolsDocumentMainWindow::closeEvent(QCloseEvent* closeEvent) { bool didClose = true; - AtomToolsDocumentSystemRequestBus::BroadcastResult(didClose, &AtomToolsDocumentSystemRequestBus::Events::CloseAllDocuments); + AtomToolsDocumentSystemRequestBus::EventResult(didClose, m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CloseAllDocuments); if (!didClose) { closeEvent->ignore(); return; } - AtomToolsMainWindowNotificationBus::Broadcast(&AtomToolsMainWindowNotifications::OnMainWindowClosing); + AtomToolsMainWindowNotificationBus::Event(m_toolId, &AtomToolsMainWindowNotifications::OnMainWindowClosing); } template diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystem.cpp similarity index 58% rename from Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.cpp rename to Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystem.cpp index 329bc1b1b3..674c9c80a0 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystem.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,7 +18,6 @@ #include #include #include -#include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT #include @@ -28,20 +28,16 @@ AZ_POP_DISABLE_WARNING namespace AtomToolsFramework { - AtomToolsDocumentSystemComponent::AtomToolsDocumentSystemComponent() + void AtomToolsDocumentSystem::Reflect(AZ::ReflectContext* context) { - } - - void AtomToolsDocumentSystemComponent::Reflect(AZ::ReflectContext* context) - { - if (AZ::SerializeContext* serialize = azrtti_cast(context)) + if (auto serialize = azrtti_cast(context)) { - serialize->Class() + serialize->Class() ->Version(0); - if (AZ::EditContext* ec = serialize->GetEditContext()) + if (auto editContext = serialize->GetEditContext()) { - ec->Class("AtomToolsDocumentSystemComponent", "") + editContext->Class("AtomToolsDocumentSystem", "") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System")) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) @@ -49,7 +45,7 @@ namespace AtomToolsFramework } } - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + if (auto behaviorContext = azrtti_cast(context)) { behaviorContext->EBus("AtomToolsDocumentSystemRequestBus") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) @@ -68,65 +64,29 @@ namespace AtomToolsFramework ->Event("SaveAllDocuments", &AtomToolsDocumentSystemRequestBus::Events::SaveAllDocuments) ->Event("GetDocumentCount", &AtomToolsDocumentSystemRequestBus::Events::GetDocumentCount) ; - - behaviorContext->EBus("AtomToolsDocumentRequestBus") - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) - ->Attribute(AZ::Script::Attributes::Category, "Editor") - ->Attribute(AZ::Script::Attributes::Module, "atomtools") - ->Event("GetAbsolutePath", &AtomToolsDocumentRequestBus::Events::GetAbsolutePath) - ->Event("Open", &AtomToolsDocumentRequestBus::Events::Open) - ->Event("Reopen", &AtomToolsDocumentRequestBus::Events::Reopen) - ->Event("Close", &AtomToolsDocumentRequestBus::Events::Close) - ->Event("Save", &AtomToolsDocumentRequestBus::Events::Save) - ->Event("SaveAsChild", &AtomToolsDocumentRequestBus::Events::SaveAsChild) - ->Event("SaveAsCopy", &AtomToolsDocumentRequestBus::Events::SaveAsCopy) - ->Event("IsOpen", &AtomToolsDocumentRequestBus::Events::IsOpen) - ->Event("IsModified", &AtomToolsDocumentRequestBus::Events::IsModified) - ->Event("IsSavable", &AtomToolsDocumentRequestBus::Events::IsSavable) - ->Event("CanUndo", &AtomToolsDocumentRequestBus::Events::CanUndo) - ->Event("CanRedo", &AtomToolsDocumentRequestBus::Events::CanRedo) - ->Event("Undo", &AtomToolsDocumentRequestBus::Events::Undo) - ->Event("Redo", &AtomToolsDocumentRequestBus::Events::Redo) - ->Event("BeginEdit", &AtomToolsDocumentRequestBus::Events::BeginEdit) - ->Event("EndEdit", &AtomToolsDocumentRequestBus::Events::EndEdit) - ; } } - void AtomToolsDocumentSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) - { - provided.push_back(AZ_CRC_CE("AtomToolsDocumentSystemService")); - } - - void AtomToolsDocumentSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + AtomToolsDocumentSystem::AtomToolsDocumentSystem(const AZ::Crc32& toolId) + : m_toolId(toolId) { - incompatible.push_back(AZ_CRC_CE("AtomToolsDocumentSystemService")); + AtomToolsDocumentSystemRequestBus::Handler::BusConnect(m_toolId); + AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId); } - void AtomToolsDocumentSystemComponent::Init() - { - } - - void AtomToolsDocumentSystemComponent::Activate() + AtomToolsDocumentSystem::~AtomToolsDocumentSystem() { m_documentMap.clear(); - AtomToolsDocumentSystemRequestBus::Handler::BusConnect(); - AtomToolsDocumentNotificationBus::Handler::BusConnect(); - } - - void AtomToolsDocumentSystemComponent::Deactivate() - { AtomToolsDocumentNotificationBus::Handler::BusDisconnect(); AtomToolsDocumentSystemRequestBus::Handler::BusDisconnect(); - m_documentMap.clear(); } - void AtomToolsDocumentSystemComponent::RegisterDocumentType(AZStd::function documentCreator) + void AtomToolsDocumentSystem::RegisterDocumentType(const AtomToolsDocumentFactoryCallback& documentCreator) { m_documentCreator = documentCreator; } - AZ::Uuid AtomToolsDocumentSystemComponent::CreateDocument() + AZ::Uuid AtomToolsDocumentSystem::CreateDocument() { if (!m_documentCreator) { @@ -134,7 +94,7 @@ namespace AtomToolsFramework return AZ::Uuid::CreateNull(); } - AZStd::unique_ptr document(m_documentCreator()); + AZStd::unique_ptr document(m_documentCreator(m_toolId)); if (!document) { AZ_Error("AtomToolsDocument", false, "Failed to create new document"); @@ -146,112 +106,17 @@ namespace AtomToolsFramework return documentId; } - bool AtomToolsDocumentSystemComponent::DestroyDocument(const AZ::Uuid& documentId) + bool AtomToolsDocumentSystem::DestroyDocument(const AZ::Uuid& documentId) { return m_documentMap.erase(documentId) != 0; } - void AtomToolsDocumentSystemComponent::OnDocumentExternallyModified(const AZ::Uuid& documentId) - { - m_documentIdsWithExternalChanges.insert(documentId); - QueueReopenDocuments(); - } - - void AtomToolsDocumentSystemComponent::OnDocumentDependencyModified(const AZ::Uuid& documentId) - { - m_documentIdsWithDependencyChanges.insert(documentId); - QueueReopenDocuments(); - } - - void AtomToolsDocumentSystemComponent::QueueReopenDocuments() - { - if (!m_queueReopenDocuments) - { - m_queueReopenDocuments = true; - QTimer::singleShot(0, [this] { ReopenDocuments(); }); - } - } - - void AtomToolsDocumentSystemComponent::ReopenDocuments() - { - const bool enableHotReload = GetSettingOrDefault("/O3DE/AtomToolsFramework/DocumentSystem/EnableHotReload", true); - if (!enableHotReload) - { - m_documentIdsWithDependencyChanges.clear(); - m_documentIdsWithExternalChanges.clear(); - m_queueReopenDocuments = false; - } - - const bool enableHotReloadPrompts = - GetSettingOrDefault("/O3DE/AtomToolsFramework/DocumentSystem/EnableHotReloadPrompts", true); - - for (const AZ::Uuid& documentId : m_documentIdsWithExternalChanges) - { - m_documentIdsWithDependencyChanges.erase(documentId); - - AZStd::string documentPath; - AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath); - - if (enableHotReloadPrompts && - (QMessageBox::question(QApplication::activeWindow(), - QString("Document was externally modified"), - QString("Would you like to reopen the document:\n%1?").arg(documentPath.c_str()), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)) - { - continue; - } - - AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount); - - bool openResult = false; - AtomToolsDocumentRequestBus::EventResult(openResult, documentId, &AtomToolsDocumentRequestBus::Events::Open, documentPath); - if (!openResult) - { - QMessageBox::critical( - QApplication::activeWindow(), QString("Document could not be opened"), - QString("Failed to open: \n%1\n\n%2").arg(documentPath.c_str()).arg(traceRecorder.GetDump().c_str())); - AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId); - } - } - - for (const AZ::Uuid& documentId : m_documentIdsWithDependencyChanges) - { - AZStd::string documentPath; - AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath); - - if (enableHotReloadPrompts && - (QMessageBox::question(QApplication::activeWindow(), - QString("Document dependencies have changed"), - QString("Would you like to update the document with these changes:\n%1?").arg(documentPath.c_str()), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)) - { - continue; - } - - AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount); - - bool openResult = false; - AtomToolsDocumentRequestBus::EventResult(openResult, documentId, &AtomToolsDocumentRequestBus::Events::Reopen); - if (!openResult) - { - QMessageBox::critical( - QApplication::activeWindow(), QString("Document could not be opened"), - QString("Failed to open: \n%1\n\n%2").arg(documentPath.c_str()).arg(traceRecorder.GetDump().c_str())); - AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId); - } - } - - m_documentIdsWithDependencyChanges.clear(); - m_documentIdsWithExternalChanges.clear(); - m_queueReopenDocuments = false; - } - - AZ::Uuid AtomToolsDocumentSystemComponent::OpenDocument(AZStd::string_view sourcePath) + AZ::Uuid AtomToolsDocumentSystem::OpenDocument(AZStd::string_view sourcePath) { return OpenDocumentImpl(sourcePath, true); } - AZ::Uuid AtomToolsDocumentSystemComponent::CreateDocumentFromFile(AZStd::string_view sourcePath, AZStd::string_view targetPath) + AZ::Uuid AtomToolsDocumentSystem::CreateDocumentFromFile(AZStd::string_view sourcePath, AZStd::string_view targetPath) { const AZ::Uuid documentId = OpenDocumentImpl(sourcePath, false); if (documentId.IsNull()) @@ -266,18 +131,18 @@ namespace AtomToolsFramework } // Send document open notification after creating new one - AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId); + AtomToolsDocumentNotificationBus::Event(m_toolId, &AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId); return documentId; } - bool AtomToolsDocumentSystemComponent::CloseDocument(const AZ::Uuid& documentId) + bool AtomToolsDocumentSystem::CloseDocument(const AZ::Uuid& documentId) { bool isOpen = false; AtomToolsDocumentRequestBus::EventResult(isOpen, documentId, &AtomToolsDocumentRequestBus::Events::IsOpen); if (!isOpen) { // immediately destroy unopened documents - AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::DestroyDocument, documentId); + DestroyDocument(documentId); return true; } @@ -289,8 +154,8 @@ namespace AtomToolsFramework if (isModified) { auto selection = QMessageBox::question(QApplication::activeWindow(), - QString("Document has unsaved changes"), - QString("Do you want to save changes to\n%1?").arg(documentPath.c_str()), + QObject::tr("Document has unsaved changes"), + QObject::tr("Do you want to save changes to\n%1?").arg(documentPath.c_str()), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if (selection == QMessageBox::Cancel) { @@ -307,23 +172,24 @@ namespace AtomToolsFramework } } - AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount); + TraceRecorder traceRecorder(m_maxMessageBoxLineCount); bool closeResult = true; AtomToolsDocumentRequestBus::EventResult(closeResult, documentId, &AtomToolsDocumentRequestBus::Events::Close); if (!closeResult) { QMessageBox::critical( - QApplication::activeWindow(), QString("Document could not be closed"), - QString("Failed to close: \n%1\n\n%2").arg(documentPath.c_str()).arg(traceRecorder.GetDump().c_str())); + QApplication::activeWindow(), + QObject::tr("Document could not be closed"), + QObject::tr("Failed to close: \n%1\n\n%2").arg(documentPath.c_str()).arg(traceRecorder.GetDump().c_str())); return false; } - AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::DestroyDocument, documentId); + DestroyDocument(documentId); return true; } - bool AtomToolsDocumentSystemComponent::CloseAllDocuments() + bool AtomToolsDocumentSystem::CloseAllDocuments() { bool result = true; auto documentMap = m_documentMap; @@ -338,7 +204,7 @@ namespace AtomToolsFramework return result; } - bool AtomToolsDocumentSystemComponent::CloseAllDocumentsExcept(const AZ::Uuid& documentId) + bool AtomToolsDocumentSystem::CloseAllDocumentsExcept(const AZ::Uuid& documentId) { bool result = true; auto documentMap = m_documentMap; @@ -356,7 +222,7 @@ namespace AtomToolsFramework return result; } - bool AtomToolsDocumentSystemComponent::SaveDocument(const AZ::Uuid& documentId) + bool AtomToolsDocumentSystem::SaveDocument(const AZ::Uuid& documentId) { AZStd::string saveDocumentPath; AtomToolsDocumentRequestBus::EventResult(saveDocumentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath); @@ -369,26 +235,30 @@ namespace AtomToolsFramework const QFileInfo saveInfo(saveDocumentPath.c_str()); if (saveInfo.exists() && !saveInfo.isWritable()) { - QMessageBox::critical(QApplication::activeWindow(), "Error", QString("Document could not be overwritten:\n%1").arg(saveDocumentPath.c_str())); + QMessageBox::critical( + QApplication::activeWindow(), + QObject::tr("Document could not be saved"), + QObject::tr("Document could not be overwritten:\n%1").arg(saveDocumentPath.c_str())); return false; } - AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount); + TraceRecorder traceRecorder(m_maxMessageBoxLineCount); bool result = false; AtomToolsDocumentRequestBus::EventResult(result, documentId, &AtomToolsDocumentRequestBus::Events::Save); if (!result) { QMessageBox::critical( - QApplication::activeWindow(), QString("Document could not be saved"), - QString("Failed to save: \n%1\n\n%2").arg(saveDocumentPath.c_str()).arg(traceRecorder.GetDump().c_str())); + QApplication::activeWindow(), + QObject::tr("Document could not be saved"), + QObject::tr("Failed to save: \n%1\n\n%2").arg(saveDocumentPath.c_str()).arg(traceRecorder.GetDump().c_str())); return false; } return true; } - bool AtomToolsDocumentSystemComponent::SaveDocumentAsCopy(const AZ::Uuid& documentId, AZStd::string_view targetPath) + bool AtomToolsDocumentSystem::SaveDocumentAsCopy(const AZ::Uuid& documentId, AZStd::string_view targetPath) { AZStd::string saveDocumentPath = targetPath; if (saveDocumentPath.empty() || !AzFramework::StringFunc::Path::Normalize(saveDocumentPath)) @@ -399,26 +269,29 @@ namespace AtomToolsFramework const QFileInfo saveInfo(saveDocumentPath.c_str()); if (saveInfo.exists() && !saveInfo.isWritable()) { - QMessageBox::critical(QApplication::activeWindow(), "Error", QString("Document could not be overwritten:\n%1").arg(saveDocumentPath.c_str())); + QMessageBox::critical(QApplication::activeWindow(), + QObject::tr("Document could not be saved"), + QObject::tr("Document could not be overwritten:\n%1").arg(saveDocumentPath.c_str())); return false; } - AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount); + TraceRecorder traceRecorder(m_maxMessageBoxLineCount); bool result = false; AtomToolsDocumentRequestBus::EventResult(result, documentId, &AtomToolsDocumentRequestBus::Events::SaveAsCopy, saveDocumentPath); if (!result) { QMessageBox::critical( - QApplication::activeWindow(), QString("Document could not be saved"), - QString("Failed to save: \n%1\n\n%2").arg(saveDocumentPath.c_str()).arg(traceRecorder.GetDump().c_str())); + QApplication::activeWindow(), + QObject::tr("Document could not be saved"), + QObject::tr("Failed to save: \n%1\n\n%2").arg(saveDocumentPath.c_str()).arg(traceRecorder.GetDump().c_str())); return false; } return true; } - bool AtomToolsDocumentSystemComponent::SaveDocumentAsChild(const AZ::Uuid& documentId, AZStd::string_view targetPath) + bool AtomToolsDocumentSystem::SaveDocumentAsChild(const AZ::Uuid& documentId, AZStd::string_view targetPath) { AZStd::string saveDocumentPath = targetPath; if (saveDocumentPath.empty() || !AzFramework::StringFunc::Path::Normalize(saveDocumentPath)) @@ -429,26 +302,30 @@ namespace AtomToolsFramework const QFileInfo saveInfo(saveDocumentPath.c_str()); if (saveInfo.exists() && !saveInfo.isWritable()) { - QMessageBox::critical(QApplication::activeWindow(), "Error", QString("Document could not be overwritten:\n%1").arg(saveDocumentPath.c_str())); + QMessageBox::critical( + QApplication::activeWindow(), + QObject::tr("Document could not be saved"), + QObject::tr("Document could not be overwritten:\n%1").arg(saveDocumentPath.c_str())); return false; } - AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount); + TraceRecorder traceRecorder(m_maxMessageBoxLineCount); bool result = false; AtomToolsDocumentRequestBus::EventResult(result, documentId, &AtomToolsDocumentRequestBus::Events::SaveAsChild, saveDocumentPath); if (!result) { QMessageBox::critical( - QApplication::activeWindow(), QString("Document could not be saved"), - QString("Failed to save: \n%1\n\n%2").arg(saveDocumentPath.c_str()).arg(traceRecorder.GetDump().c_str())); + QApplication::activeWindow(), + QObject::tr("Document could not be saved"), + QObject::tr("Failed to save: \n%1\n\n%2").arg(saveDocumentPath.c_str()).arg(traceRecorder.GetDump().c_str())); return false; } return true; } - bool AtomToolsDocumentSystemComponent::SaveAllDocuments() + bool AtomToolsDocumentSystem::SaveAllDocuments() { bool result = true; for (const auto& documentPair : m_documentMap) @@ -462,12 +339,110 @@ namespace AtomToolsFramework return result; } - AZ::u32 AtomToolsDocumentSystemComponent::GetDocumentCount() const + AZ::u32 AtomToolsDocumentSystem::GetDocumentCount() const { return aznumeric_cast(m_documentMap.size()); } - AZ::Uuid AtomToolsDocumentSystemComponent::OpenDocumentImpl(AZStd::string_view sourcePath, bool checkIfAlreadyOpen) + + void AtomToolsDocumentSystem::OnDocumentExternallyModified(const AZ::Uuid& documentId) + { + m_documentIdsWithExternalChanges.insert(documentId); + QueueReopenDocuments(); + } + + void AtomToolsDocumentSystem::OnDocumentDependencyModified(const AZ::Uuid& documentId) + { + m_documentIdsWithDependencyChanges.insert(documentId); + QueueReopenDocuments(); + } + + void AtomToolsDocumentSystem::QueueReopenDocuments() + { + if (!m_queueReopenDocuments) + { + m_queueReopenDocuments = true; + QTimer::singleShot(0, [this] { ReopenDocuments(); }); + } + } + + void AtomToolsDocumentSystem::ReopenDocuments() + { + const bool enableHotReload = GetSettingOrDefault("/O3DE/AtomToolsFramework/AtomToolsDocumentSystem/EnableHotReload", true); + if (!enableHotReload) + { + m_documentIdsWithDependencyChanges.clear(); + m_documentIdsWithExternalChanges.clear(); + m_queueReopenDocuments = false; + } + + const bool enableHotReloadPrompts = + GetSettingOrDefault("/O3DE/AtomToolsFramework/AtomToolsDocumentSystem/EnableHotReloadPrompts", true); + + for (const AZ::Uuid& documentId : m_documentIdsWithExternalChanges) + { + m_documentIdsWithDependencyChanges.erase(documentId); + + AZStd::string documentPath; + AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath); + + if (enableHotReloadPrompts && + (QMessageBox::question(QApplication::activeWindow(), + QObject::tr("Document was externally modified"), + QObject::tr("Would you like to reopen the document:\n%1?").arg(documentPath.c_str()), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)) + { + continue; + } + + TraceRecorder traceRecorder(m_maxMessageBoxLineCount); + + bool openResult = false; + AtomToolsDocumentRequestBus::EventResult(openResult, documentId, &AtomToolsDocumentRequestBus::Events::Open, documentPath); + if (!openResult) + { + QMessageBox::critical( + QApplication::activeWindow(), + QObject::tr("Document could not be opened"), + QObject::tr("Failed to open: \n%1\n\n%2").arg(documentPath.c_str()).arg(traceRecorder.GetDump().c_str())); + CloseDocument(documentId); + } + } + + for (const AZ::Uuid& documentId : m_documentIdsWithDependencyChanges) + { + AZStd::string documentPath; + AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath); + + if (enableHotReloadPrompts && + (QMessageBox::question(QApplication::activeWindow(), + QObject::tr("Document dependencies have changed"), + QObject::tr("Would you like to update the document with these changes:\n%1?").arg(documentPath.c_str()), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)) + { + continue; + } + + TraceRecorder traceRecorder(m_maxMessageBoxLineCount); + + bool openResult = false; + AtomToolsDocumentRequestBus::EventResult(openResult, documentId, &AtomToolsDocumentRequestBus::Events::Reopen); + if (!openResult) + { + QMessageBox::critical( + QApplication::activeWindow(), + QObject::tr("Document could not be opened"), + QObject::tr("Failed to open: \n%1\n\n%2").arg(documentPath.c_str()).arg(traceRecorder.GetDump().c_str())); + CloseDocument(documentId); + } + } + + m_documentIdsWithDependencyChanges.clear(); + m_documentIdsWithExternalChanges.clear(); + m_queueReopenDocuments = false; + } + + AZ::Uuid AtomToolsDocumentSystem::OpenDocumentImpl(AZStd::string_view sourcePath, bool checkIfAlreadyOpen) { AZStd::string requestedPath = sourcePath; if (requestedPath.empty()) @@ -477,7 +452,9 @@ namespace AtomToolsFramework if (!AzFramework::StringFunc::Path::Normalize(requestedPath)) { - QMessageBox::critical(QApplication::activeWindow(), "Error", QString("Document path is invalid:\n%1").arg(requestedPath.c_str())); + QMessageBox::critical(QApplication::activeWindow(), + QObject::tr("Document could not be opened"), + QObject::tr("Document path is invalid:\n%1").arg(requestedPath.c_str())); return AZ::Uuid::CreateNull(); } @@ -490,21 +467,22 @@ namespace AtomToolsFramework AtomToolsDocumentRequestBus::EventResult(openDocumentPath, documentPair.first, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath); if (openDocumentPath == requestedPath) { - AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentPair.first); + AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentPair.first); return documentPair.first; } } } - AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount); + TraceRecorder traceRecorder(m_maxMessageBoxLineCount); - AZ::Uuid documentId = AZ::Uuid::CreateNull(); - AtomToolsDocumentSystemRequestBus::BroadcastResult(documentId, &AtomToolsDocumentSystemRequestBus::Events::CreateDocument); + AZ::Uuid documentId = CreateDocument(); if (documentId.IsNull()) { QMessageBox::critical( - QApplication::activeWindow(), QString("Document could not be created"), - QString("Failed to create: \n%1\n\n%2").arg(requestedPath.c_str()).arg(traceRecorder.GetDump().c_str())); + QApplication::activeWindow(), + QObject::tr("Document could not be opened"), + QObject::tr("Failed to create: \n%1\n\n%2").arg(requestedPath.c_str()).arg(traceRecorder.GetDump().c_str())); return AZ::Uuid::CreateNull(); } @@ -513,18 +491,20 @@ namespace AtomToolsFramework if (!openResult) { QMessageBox::critical( - QApplication::activeWindow(), QString("Document could not be opened"), - QString("Failed to open: \n%1\n\n%2").arg(requestedPath.c_str()).arg(traceRecorder.GetDump().c_str())); - AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::DestroyDocument, documentId); + QApplication::activeWindow(), + QObject::tr("Document could not be opened"), + QObject::tr("Failed to open: \n%1\n\n%2").arg(requestedPath.c_str()).arg(traceRecorder.GetDump().c_str())); + DestroyDocument(documentId); return AZ::Uuid::CreateNull(); } else if (traceRecorder.GetWarningCount(true) > 0) { QMessageBox::warning( - QApplication::activeWindow(), QString("Document opened with warnings"), - QString("Warnings encountered: \n%1\n\n%2").arg(requestedPath.c_str()).arg(traceRecorder.GetDump().c_str())); + QApplication::activeWindow(), + QObject::tr("Document opened with warnings"), + QObject::tr("Warnings encountered: \n%1\n\n%2").arg(requestedPath.c_str()).arg(traceRecorder.GetDump().c_str())); } return documentId; } -} +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/DollyCameraBehavior.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/DollyCameraBehavior.cpp similarity index 63% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/DollyCameraBehavior.cpp rename to Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/DollyCameraBehavior.cpp index c69884c5c0..fa4f13fecb 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/DollyCameraBehavior.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/DollyCameraBehavior.cpp @@ -6,21 +6,25 @@ * */ +#include +#include #include #include #include -#include -#include -namespace MaterialEditor +namespace AtomToolsFramework { + DollyCameraBehavior::DollyCameraBehavior(ViewportInputBehaviorControllerInterface* controller) + : ViewportInputBehavior(controller) + { + } + void DollyCameraBehavior::TickInternal([[maybe_unused]] float x, float y, [[maybe_unused]] float z) { m_distanceToTarget = m_distanceToTarget + y; AZ::Transform transform = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(transform, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTM); - AZ::Vector3 position = m_targetPosition - - transform.GetRotation().TransformVector(AZ::Vector3::CreateAxisY(m_distanceToTarget)); + AZ::Vector3 position = m_targetPosition - transform.GetRotation().TransformVector(AZ::Vector3::CreateAxisY(m_distanceToTarget)); AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalTranslation, position); } @@ -33,4 +37,4 @@ namespace MaterialEditor { return SensitivityY; } -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/IdleBehavior.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/IdleBehavior.cpp new file mode 100644 index 0000000000..7bf2f378ce --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/IdleBehavior.cpp @@ -0,0 +1,18 @@ +/* + * 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 AtomToolsFramework +{ + IdleBehavior::IdleBehavior(ViewportInputBehaviorControllerInterface* controller) + : ViewportInputBehavior(controller) + { + } +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MoveCameraBehavior.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/MoveCameraBehavior.cpp similarity index 63% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MoveCameraBehavior.cpp rename to Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/MoveCameraBehavior.cpp index 80b3409f57..ea70b72cb4 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MoveCameraBehavior.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/MoveCameraBehavior.cpp @@ -6,27 +6,25 @@ * */ +#include +#include #include #include -#include -#include -namespace MaterialEditor +namespace AtomToolsFramework { + MoveCameraBehavior::MoveCameraBehavior(ViewportInputBehaviorControllerInterface* controller) + : ViewportInputBehavior(controller) + { + } + void MoveCameraBehavior::End() { - float distanceToTarget; - MaterialEditorViewportInputControllerRequestBus::BroadcastResult( - distanceToTarget, - &MaterialEditorViewportInputControllerRequestBus::Handler::GetDistanceToTarget); + float distanceToTarget = m_controller->GetDistanceToTarget(); AZ::Transform transform = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(transform, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTM); - AZ::Vector3 targetPosition = - transform.GetTranslation() + - transform.GetBasisY() * distanceToTarget; - MaterialEditorViewportInputControllerRequestBus::Broadcast( - &MaterialEditorViewportInputControllerRequestBus::Handler::SetTargetPosition, - targetPosition); + AZ::Vector3 targetPosition = transform.GetTranslation() + transform.GetBasisY() * distanceToTarget; + m_controller->SetTargetPosition(targetPosition); } void MoveCameraBehavior::TickInternal(float x, float y, float z) @@ -41,7 +39,7 @@ namespace MaterialEditor m_targetPosition += deltaPosition; AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalTranslation, position); - Behavior::TickInternal(x, y, z); + ViewportInputBehavior::TickInternal(x, y, z); } float MoveCameraBehavior::GetSensitivityX() @@ -53,4 +51,4 @@ namespace MaterialEditor { return SensitivityY; } -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/OrbitCameraBehavior.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/OrbitCameraBehavior.cpp similarity index 72% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/OrbitCameraBehavior.cpp rename to Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/OrbitCameraBehavior.cpp index 1d93e4eafe..33d4fb0812 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/OrbitCameraBehavior.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/OrbitCameraBehavior.cpp @@ -6,15 +6,20 @@ * */ +#include +#include #include -#include -#include -namespace MaterialEditor +namespace AtomToolsFramework { + OrbitCameraBehavior::OrbitCameraBehavior(ViewportInputBehaviorControllerInterface* controller) + : ViewportInputBehavior(controller) + { + } + void OrbitCameraBehavior::TickInternal(float x, float y, float z) { - Behavior::TickInternal(x, y, z); + ViewportInputBehavior::TickInternal(x, y, z); // don't align camera until a movement has been made so that accidental right-click doesn't reset camera if (!m_aligned) @@ -27,12 +32,9 @@ namespace MaterialEditor AZ::Quaternion rotation = transform.GetRotation(); AZ::Vector3 right = transform.GetBasisX(); rotation = - AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), -x) * - AZ::Quaternion::CreateFromAxisAngle(right, -y) * - rotation; + AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), -x) * AZ::Quaternion::CreateFromAxisAngle(right, -y) * rotation; rotation.Normalize(); - AZ::Vector3 position = - rotation.TransformVector(AZ::Vector3(0, -m_distanceToTarget, 0)) + m_targetPosition; + AZ::Vector3 position = rotation.TransformVector(AZ::Vector3(0, -m_distanceToTarget, 0)) + m_targetPosition; transform = AZ::Transform::CreateFromQuaternionAndTranslation(rotation, position); AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalTM, transform); } @@ -56,4 +58,4 @@ namespace MaterialEditor AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, targetRotation); m_aligned = true; } -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/PanCameraBehavior.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/PanCameraBehavior.cpp similarity index 61% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/PanCameraBehavior.cpp rename to Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/PanCameraBehavior.cpp index 2b036a1319..41d8586a7f 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/PanCameraBehavior.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/PanCameraBehavior.cpp @@ -6,28 +6,26 @@ * */ +#include +#include #include #include #include -#include -#include -namespace MaterialEditor +namespace AtomToolsFramework { + PanCameraBehavior::PanCameraBehavior(ViewportInputBehaviorControllerInterface* controller) + : ViewportInputBehavior(controller) + { + } + void PanCameraBehavior::End() { - float distanceToTarget; - MaterialEditorViewportInputControllerRequestBus::BroadcastResult( - distanceToTarget, - &MaterialEditorViewportInputControllerRequestBus::Handler::GetDistanceToTarget); + float distanceToTarget = m_controller->GetDistanceToTarget(); AZ::Transform transform = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(transform, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTM); - AZ::Vector3 targetPosition = - transform.GetTranslation() + - transform.GetBasisY() * distanceToTarget; - MaterialEditorViewportInputControllerRequestBus::Broadcast( - &MaterialEditorViewportInputControllerRequestBus::Handler::SetTargetPosition, - targetPosition); + AZ::Vector3 targetPosition = transform.GetTranslation() + transform.GetBasisY() * distanceToTarget; + m_controller->SetTargetPosition(targetPosition); } void PanCameraBehavior::TickInternal(float x, float y, [[maybe_unused]] float z) @@ -37,9 +35,7 @@ namespace MaterialEditor AZ::Quaternion rotation = transform.GetRotation(); const AZ::Vector3 right = transform.GetBasisX(); rotation = - AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), -x) * - AZ::Quaternion::CreateFromAxisAngle(right, -y) * - rotation; + AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), -x) * AZ::Quaternion::CreateFromAxisAngle(right, -y) * rotation; rotation.Normalize(); AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, rotation); } @@ -53,4 +49,4 @@ namespace MaterialEditor { return SensitivityY; } -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/RotateEnvironmentBehavior.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/RotateEnvironmentBehavior.cpp new file mode 100644 index 0000000000..7e8216fbda --- /dev/null +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/RotateEnvironmentBehavior.cpp @@ -0,0 +1,53 @@ +/* + * 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 + +namespace AtomToolsFramework +{ + RotateEnvironmentBehavior::RotateEnvironmentBehavior(ViewportInputBehaviorControllerInterface* controller) + : ViewportInputBehavior(controller) + { + } + + void RotateEnvironmentBehavior::Start() + { + ViewportInputBehavior::Start(); + + m_environmentEntityId = m_controller->GetEnvironmentEntityId(); + AZ_Assert(m_environmentEntityId.IsValid(), "Failed to find m_environmentEntityId"); + m_skyBoxFeatureProcessor = + AZ::RPI::Scene::GetFeatureProcessorForEntity(m_environmentEntityId); + } + + void RotateEnvironmentBehavior::TickInternal(float x, float y, float z) + { + ViewportInputBehavior::TickInternal(x, y, z); + + m_rotation += x; + AZ::Quaternion rotation = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), m_rotation); + AZ::TransformBus::Event(m_environmentEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, rotation); + const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateFromQuaternion(rotation); + m_skyBoxFeatureProcessor->SetCubemapRotationMatrix(rotationMatrix); + } + + float RotateEnvironmentBehavior::GetSensitivityX() + { + return SensitivityX; + } + + float RotateEnvironmentBehavior::GetSensitivityY() + { + return SensitivityY; + } +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateModelBehavior.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/RotateModelBehavior.cpp similarity index 63% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateModelBehavior.cpp rename to Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/RotateModelBehavior.cpp index ed09ae8fa3..2b01471789 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateModelBehavior.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/RotateModelBehavior.cpp @@ -6,25 +6,27 @@ * */ +#include +#include #include -#include -#include -namespace MaterialEditor +namespace AtomToolsFramework { + RotateModelBehavior::RotateModelBehavior(ViewportInputBehaviorControllerInterface* controller) + : ViewportInputBehavior(controller) + { + } + void RotateModelBehavior::Start() { - Behavior::Start(); + ViewportInputBehavior::Start(); - MaterialEditorViewportInputControllerRequestBus::BroadcastResult( - m_targetEntityId, - &MaterialEditorViewportInputControllerRequestBus::Handler::GetTargetEntityId); + m_targetEntityId = m_controller->GetTargetEntityId(); AZ_Assert(m_targetEntityId.IsValid(), "Failed to find m_targetEntityId"); - AZ::EntityId cameraEntityId; - MaterialEditorViewportInputControllerRequestBus::BroadcastResult( - cameraEntityId, - &MaterialEditorViewportInputControllerRequestBus::Handler::GetCameraEntityId); + + AZ::EntityId cameraEntityId = m_controller->GetCameraEntityId(); AZ_Assert(cameraEntityId.IsValid(), "Failed to find cameraEntityId"); + AZ::Transform transform = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(transform, cameraEntityId, &AZ::TransformBus::Events::GetLocalTM); m_cameraRight = transform.GetBasisX(); @@ -32,16 +34,14 @@ namespace MaterialEditor void RotateModelBehavior::TickInternal(float x, float y, float z) { - Behavior::TickInternal(x, y, z); + ViewportInputBehavior::TickInternal(x, y, z); AZ::Transform transform = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(transform, m_targetEntityId, &AZ::TransformBus::Events::GetLocalTM); AZ::Quaternion rotation = transform.GetRotation(); - rotation = - AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), x) * - AZ::Quaternion::CreateFromAxisAngle(m_cameraRight, y) * - rotation; + rotation = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), x) * + AZ::Quaternion::CreateFromAxisAngle(m_cameraRight, y) * rotation; rotation.Normalize(); AZ::TransformBus::Event(m_targetEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, rotation); @@ -56,4 +56,4 @@ namespace MaterialEditor { return SensitivityY; } -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/Behavior.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/ViewportInputBehavior.cpp similarity index 54% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/Behavior.cpp rename to Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/ViewportInputBehavior.cpp index a843c4965c..653edcecb7 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/Behavior.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/ViewportInputBehavior.cpp @@ -6,78 +6,67 @@ * */ +#include +#include #include #include -#include -#include -namespace MaterialEditor +namespace AtomToolsFramework { - Behavior::Behavior() + ViewportInputBehavior::ViewportInputBehavior(ViewportInputBehaviorControllerInterface* controller) + : m_controller(controller) { AZ::TickBus::Handler::BusConnect(); } - Behavior::~Behavior() + ViewportInputBehavior::~ViewportInputBehavior() { AZ::TickBus::Handler::BusDisconnect(); } - void Behavior::Start() + void ViewportInputBehavior::Start() { - m_x = 0; - m_y = 0; - m_z = 0; + m_x = {}; + m_y = {}; + m_z = {}; - MaterialEditorViewportInputControllerRequestBus::BroadcastResult( - m_cameraEntityId, - &MaterialEditorViewportInputControllerRequestBus::Handler::GetCameraEntityId); + m_cameraEntityId = m_controller->GetCameraEntityId(); AZ_Assert(m_cameraEntityId.IsValid(), "Failed to find m_cameraEntityId"); - MaterialEditorViewportInputControllerRequestBus::BroadcastResult( - m_distanceToTarget, - &MaterialEditorViewportInputControllerRequestBus::Handler::GetDistanceToTarget); - MaterialEditorViewportInputControllerRequestBus::BroadcastResult( - m_targetPosition, - &MaterialEditorViewportInputControllerRequestBus::Handler::GetTargetPosition); - MaterialEditorViewportInputControllerRequestBus::BroadcastResult( - m_radius, &MaterialEditorViewportInputControllerRequestBus::Handler::GetRadius); + m_distanceToTarget = m_controller->GetDistanceToTarget(); + m_targetPosition = m_controller->GetTargetPosition(); + m_radius = m_controller->GetRadius(); } - void Behavior::End() + void ViewportInputBehavior::End() { } - void Behavior::MoveX(float value) + void ViewportInputBehavior::MoveX(float value) { m_x += value * GetSensitivityX(); } - void Behavior::MoveY(float value) + void ViewportInputBehavior::MoveY(float value) { m_y += value * GetSensitivityY(); } - void Behavior::MoveZ(float value) + void ViewportInputBehavior::MoveZ(float value) { m_z += value * GetSensitivityZ(); } - bool Behavior::HasDelta() const + bool ViewportInputBehavior::HasDelta() const { - return - AZ::GetAbs(m_x) > std::numeric_limits::min() || - AZ::GetAbs(m_y) > std::numeric_limits::min() || + return AZ::GetAbs(m_x) > std::numeric_limits::min() || AZ::GetAbs(m_y) > std::numeric_limits::min() || AZ::GetAbs(m_z) > std::numeric_limits::min(); } - void Behavior::TickInternal([[maybe_unused]] float x, [[maybe_unused]] float y, float z) + void ViewportInputBehavior::TickInternal([[maybe_unused]] float x, [[maybe_unused]] float y, float z) { m_distanceToTarget = m_distanceToTarget - z; - bool isCameraCentered = false; - MaterialEditorViewportInputControllerRequestBus::BroadcastResult( - isCameraCentered, - &MaterialEditorViewportInputControllerRequestBus::Handler::IsCameraCentered); + bool isCameraCentered = m_controller->IsCameraCentered(); // if camera is looking at the model (locked to the model) we don't want to zoom past the model's center if (isCameraCentered) @@ -87,40 +76,35 @@ namespace MaterialEditor AZ::Transform transform = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(transform, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTM); - AZ::Vector3 position = m_targetPosition - - transform.GetRotation().TransformVector(AZ::Vector3::CreateAxisY(m_distanceToTarget)); + AZ::Vector3 position = m_targetPosition - transform.GetRotation().TransformVector(AZ::Vector3::CreateAxisY(m_distanceToTarget)); AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalTranslation, position); // if camera is not locked to the model, move its focal point so we can free look if (!isCameraCentered) { m_targetPosition += transform.GetRotation().TransformVector(AZ::Vector3::CreateAxisY(z)); - MaterialEditorViewportInputControllerRequestBus::Broadcast( - &MaterialEditorViewportInputControllerRequestBus::Handler::SetTargetPosition, - m_targetPosition); - MaterialEditorViewportInputControllerRequestBus::BroadcastResult( - m_distanceToTarget, - &MaterialEditorViewportInputControllerRequestBus::Handler::GetDistanceToTarget); + m_controller->SetTargetPosition(m_targetPosition); + m_distanceToTarget = m_controller->GetDistanceToTarget(); } } - float Behavior::GetSensitivityX() + float ViewportInputBehavior::GetSensitivityX() { return 0; } - float Behavior::GetSensitivityY() + float ViewportInputBehavior::GetSensitivityY() { return 0; } - float Behavior::GetSensitivityZ() + float ViewportInputBehavior::GetSensitivityZ() { // adjust zooming sensitivity by model size, so that large models zoom at the same speed as smaller ones return 0.001f * AZ::GetMax(0.5f, m_radius); } - AZ::Quaternion Behavior::LookRotation(AZ::Vector3 forward) + AZ::Quaternion ViewportInputBehavior::LookRotation(AZ::Vector3 forward) { forward.Normalize(); AZ::Vector3 right = forward.CrossZAxis(); @@ -132,7 +116,7 @@ namespace MaterialEditor return rotation; } - float Behavior::TakeStep(float& value, float t) + float ViewportInputBehavior::TakeStep(float& value, float t) { const float absValue = AZ::GetAbs(value); float step; @@ -148,7 +132,7 @@ namespace MaterialEditor return step; } - void Behavior::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + void ViewportInputBehavior::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { // delta x and y values are accumulated in MoveX and MoveY functions (by dragging the mouse) // in the Tick function we then lerp them down to 0 over short time and apply delta transform to an entity @@ -163,4 +147,4 @@ namespace MaterialEditor TickInternal(x, y, z); } } -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/ViewportInputBehaviorController.cpp similarity index 53% rename from Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp rename to Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/ViewportInputBehaviorController.cpp index ced59f1bf5..3e0398f08e 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ViewportInputBehaviorController/ViewportInputBehaviorController.cpp @@ -6,120 +6,94 @@ * */ -#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 - -namespace MaterialEditor +#include +#include + +namespace AtomToolsFramework { - MaterialEditorViewportInputController::MaterialEditorViewportInputController() + ViewportInputBehaviorController::ViewportInputBehaviorController( + const AZ::EntityId& cameraEntityId, const AZ::EntityId& targetEntityId, const AZ::EntityId& environmentEntityId) : AzFramework::SingleViewportController() + , m_cameraEntityId(cameraEntityId) + , m_targetEntityId(targetEntityId) + , m_environmentEntityId(environmentEntityId) , m_targetPosition(AZ::Vector3::CreateZero()) { - m_behaviorMap[None] = AZStd::make_shared(); - m_behaviorMap[Lmb] = AZStd::make_shared(); - m_behaviorMap[Mmb] = AZStd::make_shared(); - m_behaviorMap[Rmb] = AZStd::make_shared(); - m_behaviorMap[Alt ^ Lmb] = AZStd::make_shared(); - m_behaviorMap[Alt ^ Mmb] = AZStd::make_shared(); - m_behaviorMap[Alt ^ Rmb] = AZStd::make_shared(); - m_behaviorMap[Lmb ^ Rmb] = AZStd::make_shared(); - m_behaviorMap[Ctrl ^ Lmb] = AZStd::make_shared(); - m_behaviorMap[Shift ^ Lmb] = AZStd::make_shared(); } - MaterialEditorViewportInputController::~MaterialEditorViewportInputController() + ViewportInputBehaviorController::~ViewportInputBehaviorController() { - if (m_initialized) - { - MaterialEditorViewportInputControllerRequestBus::Handler::BusDisconnect(); - } } - void MaterialEditorViewportInputController::Init(const AZ::EntityId& cameraEntityId, const AZ::EntityId& targetEntityId, const AZ::EntityId& iblEntityId) + void ViewportInputBehaviorController::AddBehavior(KeyMask mask, AZStd::shared_ptr behavior) { - if (m_initialized) - { - AZ_Error("MaterialEditorViewportInputController", false, "Controller already initialized."); - return; - } - m_initialized = true; - m_cameraEntityId = cameraEntityId; - m_targetEntityId = targetEntityId; - m_iblEntityId = iblEntityId; - - MaterialEditorViewportInputControllerRequestBus::Handler::BusConnect(); + m_behaviorMap[mask] = behavior; } - const AZ::EntityId& MaterialEditorViewportInputController::GetCameraEntityId() const + const AZ::EntityId& ViewportInputBehaviorController::GetCameraEntityId() const { return m_cameraEntityId; } - const AZ::EntityId& MaterialEditorViewportInputController::GetTargetEntityId() const + const AZ::EntityId& ViewportInputBehaviorController::GetTargetEntityId() const { return m_targetEntityId; } - const AZ::EntityId& MaterialEditorViewportInputController::GetIblEntityId() const + const AZ::EntityId& ViewportInputBehaviorController::GetEnvironmentEntityId() const { - return m_iblEntityId; + return m_environmentEntityId; } - const AZ::Vector3& MaterialEditorViewportInputController::GetTargetPosition() const + const AZ::Vector3& ViewportInputBehaviorController::GetTargetPosition() const { return m_targetPosition; } - void MaterialEditorViewportInputController::SetTargetPosition(const AZ::Vector3& targetPosition) + void ViewportInputBehaviorController::SetTargetPosition(const AZ::Vector3& targetPosition) { m_targetPosition = targetPosition; m_isCameraCentered = false; } - float MaterialEditorViewportInputController::GetDistanceToTarget() const + void ViewportInputBehaviorController::SetTargetBounds(const AZ::Aabb& targetBounds) + { + m_targetBounds = targetBounds; + } + + float ViewportInputBehaviorController::GetDistanceToTarget() const { AZ::Vector3 cameraPosition; AZ::TransformBus::EventResult(cameraPosition, m_cameraEntityId, &AZ::TransformBus::Events::GetLocalTranslation); return cameraPosition.GetDistance(m_targetPosition); } - void MaterialEditorViewportInputController::GetExtents(float& distanceMin, float& distanceMax) const + void ViewportInputBehaviorController::GetExtents(float& distanceMin, float& distanceMax) const { distanceMin = m_distanceMin; distanceMax = m_distanceMax; } - float MaterialEditorViewportInputController::GetRadius() const + float ViewportInputBehaviorController::GetRadius() const { return m_radius; } - void MaterialEditorViewportInputController::UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event) + void ViewportInputBehaviorController::UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event) { if (m_keysChanged) { @@ -135,7 +109,7 @@ namespace MaterialEditor } } - bool MaterialEditorViewportInputController::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) + bool ViewportInputBehaviorController::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) { using namespace AzFramework; @@ -145,8 +119,7 @@ namespace MaterialEditor bool mouseOver = false; AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::EventResult( - mouseOver, GetViewportId(), - &AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Events::IsMouseOver); + mouseOver, GetViewportId(), &AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Events::IsMouseOver); if (!m_behavior) { @@ -180,20 +153,17 @@ namespace MaterialEditor { m_keys |= Shift; } - if (inputChannelId == InputDeviceMouse::Movement::X) + if (m_behavior && inputChannelId == InputDeviceMouse::Movement::X) { m_behavior->MoveX(event.m_inputChannel.GetValue()); } - else if (inputChannelId == InputDeviceMouse::Movement::Y) + else if (m_behavior && inputChannelId == InputDeviceMouse::Movement::Y) { m_behavior->MoveY(event.m_inputChannel.GetValue()); } - else if (inputChannelId == InputDeviceMouse::Movement::Z) + else if (m_behavior && inputChannelId == InputDeviceMouse::Movement::Z && mouseOver) { - if (mouseOver) - { - m_behavior->MoveZ(event.m_inputChannel.GetValue()); - } + m_behavior->MoveZ(event.m_inputChannel.GetValue()); } break; case InputChannel::State::Ended: @@ -232,20 +202,17 @@ namespace MaterialEditor } break; case InputChannel::State::Updated: - if (inputChannelId == InputDeviceMouse::Movement::X) + if (m_behavior && inputChannelId == InputDeviceMouse::Movement::X) { m_behavior->MoveX(event.m_inputChannel.GetValue()); } - else if (inputChannelId == InputDeviceMouse::Movement::Y) + else if (m_behavior && inputChannelId == InputDeviceMouse::Movement::Y) { m_behavior->MoveY(event.m_inputChannel.GetValue()); } - else if (inputChannelId == InputDeviceMouse::Movement::Z) + else if (m_behavior && inputChannelId == InputDeviceMouse::Movement::Z && mouseOver) { - if (mouseOver) - { - m_behavior->MoveZ(event.m_inputChannel.GetValue()); - } + m_behavior->MoveZ(event.m_inputChannel.GetValue()); } break; } @@ -258,7 +225,7 @@ namespace MaterialEditor return false; } - void MaterialEditorViewportInputController::Reset() + void ViewportInputBehaviorController::Reset() { CalculateExtents(); @@ -277,12 +244,13 @@ namespace MaterialEditor AZ::TransformBus::Event(m_targetEntityId, &AZ::TransformBus::Events::SetLocalTM, modelTransform); // reset environment - AZ::Transform iblTransform = AZ::Transform::CreateIdentity(); - AZ::TransformBus::Event(m_iblEntityId, &AZ::TransformBus::Events::SetLocalTM, iblTransform); + AZ::Transform environmentTransform = AZ::Transform::CreateIdentity(); + AZ::TransformBus::Event(m_environmentEntityId, &AZ::TransformBus::Events::SetLocalTM, environmentTransform); const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateIdentity(); - auto skyBoxFeatureProcessorInterface = AZ::RPI::Scene::GetFeatureProcessorForEntity(m_iblEntityId); - skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix); + auto skyBoxFeatureProcessor = + AZ::RPI::Scene::GetFeatureProcessorForEntity(m_environmentEntityId); + skyBoxFeatureProcessor->SetCubemapRotationMatrix(rotationMatrix); if (m_behavior) { @@ -291,62 +259,48 @@ namespace MaterialEditor } } - void MaterialEditorViewportInputController::SetFieldOfView(float value) + void ViewportInputBehaviorController::SetFieldOfView(float value) { Camera::CameraRequestBus::Event(m_cameraEntityId, &Camera::CameraRequestBus::Events::SetFovDegrees, value); } - bool MaterialEditorViewportInputController::IsCameraCentered() const + bool ViewportInputBehaviorController::IsCameraCentered() const { return m_isCameraCentered; } - void MaterialEditorViewportInputController::CalculateExtents() + void ViewportInputBehaviorController::CalculateExtents() { AZ::TransformBus::EventResult(m_modelCenter, m_targetEntityId, &AZ::TransformBus::Events::GetLocalTranslation); - - AZ::Data::AssetId modelAssetId; - AZ::Render::MeshComponentRequestBus::EventResult(modelAssetId, m_targetEntityId, - &AZ::Render::MeshComponentRequestBus::Events::GetModelAssetId); - - if (modelAssetId.IsValid()) - { - AZ::Data::Asset modelAsset = AZ::Data::AssetManager::Instance().GetAsset(modelAssetId, azrtti_typeid(), AZ::Data::AssetLoadBehavior::PreLoad); - modelAsset.BlockUntilLoadComplete(); - if (modelAsset.IsReady()) - { - const AZ::Aabb& aabb = modelAsset->GetAabb(); - aabb.GetAsSphere(m_modelCenter, m_radius); - - m_distanceMin = 0.5f * AZ::GetMin(AZ::GetMin(aabb.GetExtents().GetX(), aabb.GetExtents().GetY()), aabb.GetExtents().GetZ()) + DepthNear; - m_distanceMax = m_radius * MaxDistanceMultiplier; - } - } + m_targetBounds.GetAsSphere(m_modelCenter, m_radius); + m_distanceMin = m_targetBounds.GetExtents().GetMinElement() * 0.5f + DepthNear; + m_distanceMax = m_radius * MaxDistanceMultiplier; } - void MaterialEditorViewportInputController::EvaluateControlBehavior() + void ViewportInputBehaviorController::EvaluateControlBehavior() { - AZStd::shared_ptr nextBehavior; auto it = m_behaviorMap.find(m_keys); if (it == m_behaviorMap.end()) { - nextBehavior = m_behaviorMap[None]; - } - else - { - nextBehavior = it->second; + it = m_behaviorMap.find(None); } - if (nextBehavior == m_behavior) - { - return; - } + AZStd::shared_ptr nextBehavior = + it != m_behaviorMap.end() ? it->second : AZStd::shared_ptr(); - if (m_behavior) + if (m_behavior != nextBehavior) { - m_behavior->End(); + if (m_behavior) + { + m_behavior->End(); + } + + m_behavior = nextBehavior; + + if (m_behavior) + { + m_behavior->Start(); + } } - m_behavior = nextBehavior; - m_behavior->Start(); } -} // namespace MaterialEditor +} // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Window/AtomToolsMainWindow.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Window/AtomToolsMainWindow.cpp index b6d519bd84..bdea04fc46 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Window/AtomToolsMainWindow.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Window/AtomToolsMainWindow.cpp @@ -6,8 +6,11 @@ * */ +#include #include #include +#include +#include #include #include @@ -19,11 +22,11 @@ namespace AtomToolsFramework { - AtomToolsMainWindow::AtomToolsMainWindow(QWidget* parent) + AtomToolsMainWindow::AtomToolsMainWindow(const AZ::Crc32& toolId, QWidget* parent) : AzQtComponents::DockMainWindow(parent) + , m_toolId(toolId) + , m_advancedDockManager(new AzQtComponents::FancyDocking(this)) { - m_advancedDockManager = new AzQtComponents::FancyDocking(this); - setDockNestingEnabled(true); setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea); setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); @@ -42,20 +45,21 @@ namespace AtomToolsFramework centralWidget->setLayout(centralWidgetLayout); setCentralWidget(centralWidget); - m_assetBrowser = new AtomToolsFramework::AtomToolsAssetBrowser(this); + m_assetBrowser = new AtomToolsAssetBrowser(this); AddDockWidget("Asset Browser", m_assetBrowser, Qt::BottomDockWidgetArea, Qt::Horizontal); AddDockWidget("Python Terminal", new AzToolsFramework::CScriptTermDialog, Qt::BottomDockWidgetArea, Qt::Horizontal); SetDockWidgetVisible("Python Terminal", false); SetupMetrics(); + UpdateWindowTitle(); + resize(1280, 1024); - AtomToolsMainWindowRequestBus::Handler::BusConnect(); + AtomToolsMainWindowRequestBus::Handler::BusConnect(m_toolId); } AtomToolsMainWindow::~AtomToolsMainWindow() { - AtomToolsFramework::PerformanceMonitorRequestBus::Broadcast( - &AtomToolsFramework::PerformanceMonitorRequestBus::Handler::SetProfilerEnabled, false); + PerformanceMonitorRequestBus::Broadcast(&PerformanceMonitorRequestBus::Handler::SetProfilerEnabled, false); AtomToolsMainWindowRequestBus::Handler::BusDisconnect(); } @@ -160,10 +164,12 @@ namespace AtomToolsFramework m_menuHelp = menuBar()->addMenu("&Help"); m_menuFile->addAction("Run &Python...", [this]() { - const QString script = QFileDialog::getOpenFileName(this, "Run Script", QString(), QString("*.py")); + const QString script = QFileDialog::getOpenFileName( + this, QObject::tr("Run Script"), QString(AZ::Utils::GetProjectPath().c_str()), QString("*.py")); if (!script.isEmpty()) { - AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilename, script.toUtf8().constData()); + AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast( + &AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilename, script.toUtf8().constData()); } }); @@ -213,21 +219,34 @@ namespace AtomToolsFramework m_metricsTimer.start(); connect(&m_metricsTimer, &QTimer::timeout, this, &AtomToolsMainWindow::UpdateMetrics); - AtomToolsFramework::PerformanceMonitorRequestBus::Broadcast( - &AtomToolsFramework::PerformanceMonitorRequestBus::Handler::SetProfilerEnabled, true); + PerformanceMonitorRequestBus::Broadcast(&PerformanceMonitorRequestBus::Handler::SetProfilerEnabled, true); UpdateMetrics(); } void AtomToolsMainWindow::UpdateMetrics() { - AtomToolsFramework::PerformanceMetrics metrics = {}; - AtomToolsFramework::PerformanceMonitorRequestBus::BroadcastResult( - metrics, &AtomToolsFramework::PerformanceMonitorRequestBus::Handler::GetMetrics); + PerformanceMetrics metrics = {}; + PerformanceMonitorRequestBus::BroadcastResult(metrics, &PerformanceMonitorRequestBus::Handler::GetMetrics); m_statusBarCpuTime->setText(tr("CPU Time %1 ms").arg(QString::number(metrics.m_cpuFrameTimeMs, 'f', 2))); m_statusBarGpuTime->setText(tr("GPU Time %1 ms").arg(QString::number(metrics.m_gpuFrameTimeMs, 'f', 2))); int frameRate = metrics.m_cpuFrameTimeMs > 0 ? aznumeric_cast(1000 / metrics.m_cpuFrameTimeMs) : 0; m_statusBarFps->setText(tr("FPS %1").arg(QString::number(frameRate))); } + + void AtomToolsMainWindow::UpdateWindowTitle() + { + AZ::Name apiName = AZ::RHI::Factory::Get().GetName(); + if (!apiName.IsEmpty()) + { + QString title = QString{ "%1 (%2)" }.arg(QApplication::applicationName()).arg(apiName.GetCStr()); + setWindowTitle(title); + } + else + { + AZ_Assert(false, "Render API name not found"); + setWindowTitle(QApplication::applicationName()); + } + } } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Window/AtomToolsMainWindowSystemComponent.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Window/AtomToolsMainWindowSystemComponent.cpp index 3114a5d9f7..c9fce9f86b 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Window/AtomToolsMainWindowSystemComponent.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Window/AtomToolsMainWindowSystemComponent.cpp @@ -6,7 +6,6 @@ * */ -#include #include #include #include @@ -25,14 +24,6 @@ namespace AtomToolsFramework if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { - behaviorContext->EBus("AtomToolsMainWindowFactoryRequestBus") - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) - ->Attribute(AZ::Script::Attributes::Category, "Editor") - ->Attribute(AZ::Script::Attributes::Module, "atomtools") - ->Event("CreateMainWindow", &AtomToolsMainWindowFactoryRequestBus::Events::CreateMainWindow) - ->Event("DestroyMainWindow", &AtomToolsMainWindowFactoryRequestBus::Events::DestroyMainWindow) - ; - behaviorContext->EBus("AtomToolsMainWindowRequestBus") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) ->Attribute(AZ::Script::Attributes::Category, "Editor") diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake b/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake index 041e6e1807..5046e28145 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake @@ -15,6 +15,7 @@ set(FILES Include/AtomToolsFramework/Communication/LocalSocket.h Include/AtomToolsFramework/Debug/TraceRecorder.h Include/AtomToolsFramework/Document/AtomToolsDocument.h + Include/AtomToolsFramework/Document/AtomToolsDocumentSystem.h Include/AtomToolsFramework/Document/AtomToolsDocumentApplication.h Include/AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h Include/AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h @@ -38,7 +39,6 @@ set(FILES Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h Include/AtomToolsFramework/Window/AtomToolsMainWindow.h Include/AtomToolsFramework/Window/AtomToolsMainWindowRequestBus.h - Include/AtomToolsFramework/Window/AtomToolsMainWindowFactoryRequestBus.h Include/AtomToolsFramework/Window/AtomToolsMainWindowNotificationBus.h Source/Application/AtomToolsApplication.cpp Source/AssetBrowser/AtomToolsAssetBrowser.cpp @@ -53,8 +53,7 @@ set(FILES Source/Document/AtomToolsDocument.cpp Source/Document/AtomToolsDocumentApplication.cpp Source/Document/AtomToolsDocumentMainWindow.cpp - Source/Document/AtomToolsDocumentSystemComponent.cpp - Source/Document/AtomToolsDocumentSystemComponent.h + Source/Document/AtomToolsDocumentSystem.cpp Source/DynamicProperty/DynamicProperty.cpp Source/DynamicProperty/DynamicPropertyGroup.cpp Source/Inspector/InspectorWidget.cpp @@ -89,4 +88,23 @@ set(FILES Source/PreviewRenderer/PreviewRendererCaptureState.h Source/PreviewRenderer/PreviewRendererSystemComponent.cpp Source/PreviewRenderer/PreviewRendererSystemComponent.h + Source/Viewport/ViewportInputBehaviorController/ViewportInputBehaviorController.cpp + Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/ViewportInputBehaviorController.h + Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/ViewportInputBehaviorControllerInterface.h + Source/Viewport/ViewportInputBehaviorController/ViewportInputBehavior.cpp + Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/ViewportInputBehavior.h + Source/Viewport/ViewportInputBehaviorController/DollyCameraBehavior.cpp + Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/DollyCameraBehavior.h + Source/Viewport/ViewportInputBehaviorController/IdleBehavior.cpp + Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/IdleBehavior.h + Source/Viewport/ViewportInputBehaviorController/MoveCameraBehavior.cpp + Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/MoveCameraBehavior.h + Source/Viewport/ViewportInputBehaviorController/PanCameraBehavior.cpp + Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/PanCameraBehavior.h + Source/Viewport/ViewportInputBehaviorController/OrbitCameraBehavior.cpp + Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/OrbitCameraBehavior.h + Source/Viewport/ViewportInputBehaviorController/RotateEnvironmentBehavior.cpp + Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/RotateEnvironmentBehavior.h + Source/Viewport/ViewportInputBehaviorController/RotateModelBehavior.cpp + Include/AtomToolsFramework/Viewport/ViewportInputBehaviorController/RotateModelBehavior.h ) \ No newline at end of file diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index 13b2519368..0f750ba647 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -13,17 +13,39 @@ #include #include #include -#include #include +#include #include #include #include +#include +#include +#include #include namespace MaterialEditor { - MaterialDocument::MaterialDocument() - : AtomToolsFramework::AtomToolsDocument() + void MaterialDocument::Reflect(AZ::ReflectContext* context) + { + if (auto serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(0); + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("MaterialDocumentRequestBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "Editor") + ->Attribute(AZ::Script::Attributes::Module, "materialeditor") + ->Event("SetPropertyValue", &MaterialDocumentRequestBus::Events::SetPropertyValue) + ->Event("GetPropertyValue", &MaterialDocumentRequestBus::Events::GetPropertyValue); + } + } + + MaterialDocument::MaterialDocument(const AZ::Crc32& toolId) + : AtomToolsFramework::AtomToolsDocument(toolId) { MaterialDocumentRequestBus::Handler::BusConnect(m_id); } @@ -87,12 +109,12 @@ namespace MaterialEditor } } - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentObjectInfoChanged, m_id, + AtomToolsFramework::AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentObjectInfoChanged, m_id, GetObjectInfoFromDynamicPropertyGroup(group.get()), false); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentModified, m_id); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentModified, m_id); return false; } } @@ -845,8 +867,8 @@ namespace MaterialEditor if (groupChange || groupRebuilt) { - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentObjectInfoChanged, m_id, + AtomToolsFramework::AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentObjectInfoChanged, m_id, GetObjectInfoFromDynamicPropertyGroup(group.get()), groupRebuilt); } return true; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h index 42385cfb25..ef67fb811b 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h @@ -29,11 +29,14 @@ namespace MaterialEditor , private AZ::TickBus::Handler { public: - AZ_RTTI(MaterialDocument, "{DBA269AE-892B-415C-8FA1-166B94B0E045}"); + AZ_RTTI(MaterialDocument, "{90299628-AD02-4FEB-9527-7278FA2817AD}", AtomToolsFramework::AtomToolsDocument); AZ_CLASS_ALLOCATOR(MaterialDocument, AZ::SystemAllocator, 0); - AZ_DISABLE_COPY(MaterialDocument); + AZ_DISABLE_COPY_MOVE(MaterialDocument); - MaterialDocument(); + static void Reflect(AZ::ReflectContext* context); + + MaterialDocument() = default; + MaterialDocument(const AZ::Crc32& toolId); virtual ~MaterialDocument(); // AtomToolsFramework::AtomToolsDocument overrides... diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.cpp index 17453bf7af..b4137f2d02 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.cpp @@ -42,24 +42,26 @@ void InitMaterialEditorResources() namespace MaterialEditor { + static const char* GetBuildTargetName() + { +#if !defined(LY_CMAKE_TARGET) +#error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target" +#endif + return LY_CMAKE_TARGET; + } + MaterialEditorApplication::MaterialEditorApplication(int* argc, char*** argv) - : Base(argc, argv) + : Base(GetBuildTargetName(), argc, argv) { InitMaterialEditorResources(); QApplication::setApplicationName("O3DE Material Editor"); - // The settings registry has been created at this point, so add the CMake target - AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization( - *AZ::SettingsRegistry::Get(), GetBuildTargetName()); - AzToolsFramework::EditorWindowRequestBus::Handler::BusConnect(); - AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusConnect(); } MaterialEditorApplication::~MaterialEditorApplication() { - AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusDisconnect(); AzToolsFramework::EditorWindowRequestBus::Handler::BusDisconnect(); m_window.reset(); } @@ -67,17 +69,8 @@ namespace MaterialEditor void MaterialEditorApplication::Reflect(AZ::ReflectContext* context) { Base::Reflect(context); + MaterialDocument::Reflect(context); MaterialEditorWindowSettings::Reflect(context); - - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->EBus("MaterialDocumentRequestBus") - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) - ->Attribute(AZ::Script::Attributes::Category, "Editor") - ->Attribute(AZ::Script::Attributes::Module, "materialeditor") - ->Event("SetPropertyValue", &MaterialDocumentRequestBus::Events::SetPropertyValue) - ->Event("GetPropertyValue", &MaterialDocumentRequestBus::Events::GetPropertyValue); - } } void MaterialEditorApplication::CreateStaticModules(AZStd::vector& outModules) @@ -101,35 +94,20 @@ namespace MaterialEditor { Base::StartCommon(systemEntity); - AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Handler::RegisterDocumentType, - []() { return aznew MaterialDocument(); }); - } + AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Handler::RegisterDocumentType, + [](const AZ::Crc32& toolId) { return aznew MaterialDocument(toolId); }); - AZStd::string MaterialEditorApplication::GetBuildTargetName() const - { -#if !defined(LY_CMAKE_TARGET) -#error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target" -#endif - //! Returns the build system target name of "MaterialEditor" - return AZStd::string{ LY_CMAKE_TARGET }; - } + m_window.reset(aznew MaterialEditorWindow(m_toolId)); - AZStd::vector MaterialEditorApplication::GetCriticalAssetFilters() const - { - return AZStd::vector({ "passes/", "config/", "MaterialEditor/" }); - } - - void MaterialEditorApplication::CreateMainWindow() - { - m_window.reset(aznew MaterialEditorWindow); m_assetBrowserInteractions.reset(aznew AtomToolsFramework::AtomToolsAssetBrowserInteractions); + m_assetBrowserInteractions->RegisterContextMenuActions( [](const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries) { return entries.front()->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Source; }, - []([[maybe_unused]] QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries) + [this]([[maybe_unused]] QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries) { const bool isMaterial = AzFramework::StringFunc::Path::IsExtension( entries.front()->GetFullPath().c_str(), AZ::RPI::MaterialSourceData::Extension); @@ -137,17 +115,17 @@ namespace MaterialEditor entries.front()->GetFullPath().c_str(), AZ::RPI::MaterialTypeSourceData::Extension); if (isMaterial || isMaterialType) { - menu->addAction(QObject::tr("Open"), [entries]() + menu->addAction(QObject::tr("Open"), [entries, this]() { - AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, + AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, entries.front()->GetFullPath()); }); const QString createActionName = isMaterialType ? QObject::tr("Create Material...") : QObject::tr("Create Child Material..."); - menu->addAction(createActionName, [entries]() + menu->addAction(createActionName, [entries, this]() { const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo( QString(AZ::Utils::GetProjectPath().c_str()) + @@ -155,8 +133,8 @@ namespace MaterialEditor AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." + AZ::RPI::MaterialSourceData::Extension).absoluteFilePath(); - AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile, + AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile, entries.front()->GetFullPath(), AtomToolsFramework::GetSaveFileInfo(defaultPath).absoluteFilePath().toUtf8().constData()); }); @@ -175,9 +153,9 @@ namespace MaterialEditor { return entries.front()->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Folder; }, - [](QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries) + [this](QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries) { - menu->addAction(QObject::tr("Create Material..."), [caller, entries]() + menu->addAction(QObject::tr("Create Material..."), [caller, entries, this]() { CreateMaterialDialog createDialog(entries.front()->GetFullPath().c_str(), caller); createDialog.adjustSize(); @@ -186,8 +164,8 @@ namespace MaterialEditor !createDialog.m_materialFileInfo.absoluteFilePath().isEmpty() && !createDialog.m_materialTypeFileInfo.absoluteFilePath().isEmpty()) { - AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile, + AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile, createDialog.m_materialTypeFileInfo.absoluteFilePath().toUtf8().constData(), createDialog.m_materialFileInfo.absoluteFilePath().toUtf8().constData()); } @@ -195,9 +173,15 @@ namespace MaterialEditor }); } - void MaterialEditorApplication::DestroyMainWindow() + void MaterialEditorApplication::Destroy() { m_window.reset(); + Base::Destroy(); + } + + AZStd::vector MaterialEditorApplication::GetCriticalAssetFilters() const + { + return AZStd::vector({ "passes/", "config/", "MaterialEditor/" }); } QWidget* MaterialEditorApplication::GetAppMainWindow() diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.h index 0cf4354f6c..4829d31673 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.h @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -21,7 +20,6 @@ namespace MaterialEditor class MaterialEditorApplication : public AtomToolsFramework::AtomToolsDocumentApplication , private AzToolsFramework::EditorWindowRequestBus::Handler - , private AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler { public: AZ_TYPE_INFO(MaterialEditor::MaterialEditorApplication, "{30F90CA5-1253-49B5-8143-19CEE37E22BB}"); @@ -36,15 +34,11 @@ namespace MaterialEditor void CreateStaticModules(AZStd::vector& outModules) override; const char* GetCurrentConfigurationName() const override; void StartCommon(AZ::Entity* systemEntity) override; + void Destroy() override; // AtomToolsFramework::AtomToolsApplication overrides... - AZStd::string GetBuildTargetName() const override; AZStd::vector GetCriticalAssetFilters() const override; - // AtomToolsMainWindowFactoryRequestBus::Handler overrides... - void CreateMainWindow() override; - void DestroyMainWindow() override; - // AzToolsFramework::EditorWindowRequests::Bus::Handler QWidget* GetAppMainWindow() override; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/IdleBehavior.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/IdleBehavior.cpp deleted file mode 100644 index 0cd062d86d..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/IdleBehavior.cpp +++ /dev/null @@ -1,13 +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 - -namespace MaterialEditor -{ -} // namespace MaterialEditor diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateEnvironmentBehavior.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateEnvironmentBehavior.cpp deleted file mode 100644 index 894841becd..0000000000 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateEnvironmentBehavior.cpp +++ /dev/null @@ -1,51 +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 - -namespace MaterialEditor -{ - void RotateEnvironmentBehavior::Start() - { - Behavior::Start(); - - MaterialEditorViewportInputControllerRequestBus::BroadcastResult( - m_iblEntityId, - &MaterialEditorViewportInputControllerRequestBus::Handler::GetIblEntityId); - AZ_Assert(m_iblEntityId.IsValid(), "Failed to find m_iblEntityId"); - m_skyBoxFeatureProcessorInterface = AZ::RPI::Scene::GetFeatureProcessorForEntity(m_iblEntityId); - } - - void RotateEnvironmentBehavior::TickInternal(float x, float y, float z) - { - Behavior::TickInternal(x, y, z); - - m_rotation += x; - AZ::Quaternion rotation = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), m_rotation); - AZ::TransformBus::Event(m_iblEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, rotation); - const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateFromQuaternion(rotation); - m_skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix); - } - - float RotateEnvironmentBehavior::GetSensitivityX() - { - return SensitivityX; - } - - float RotateEnvironmentBehavior::GetSensitivityY() - { - return SensitivityY; - } -} // namespace MaterialEditor diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.cpp index f25d83556d..613dace10c 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportComponent.cpp @@ -394,7 +394,7 @@ namespace MaterialEditor return m_viewportSettings->m_displayMapperOperationType; } - inline void MaterialViewportComponent::OnAssetReady(AZ::Data::Asset asset) + void MaterialViewportComponent::OnAssetReady(AZ::Data::Asset asset) { if (AZ::Data::Asset anyAsset = asset) { diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp index 21b01b5dab..c411e67612 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp @@ -40,6 +40,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -60,10 +67,10 @@ namespace MaterialEditor { static constexpr float DepthNear = 0.01f; - MaterialViewportWidget::MaterialViewportWidget(QWidget* parent) + MaterialViewportWidget::MaterialViewportWidget(const AZ::Crc32& toolId, QWidget* parent) : AtomToolsFramework::RenderViewportWidget(parent) , m_ui(new Ui::MaterialViewportWidget) - , m_viewportController(AZStd::make_shared()) + , m_toolId(toolId) { m_ui->setupUi(this); @@ -88,9 +95,15 @@ namespace MaterialEditor AZ_Assert(mainScene, "Main scenes missing during system component initialization"); mainScene->SetSubsystem(m_scene); - // Create a render pipeline from the specified asset for the window context and add the pipeline to the scene + // Load the render pipeline asset AZ::Data::Asset pipelineAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath( m_defaultPipelineAssetPath.c_str(), AZ::RPI::AssetUtils::TraceLevel::Error); + + // The default pipeline determines the initial MSAA state for the application + const AZ::RPI::RenderPipelineDescriptor* renderPipelineDescriptor = AZ::RPI::GetDataFromAnyAsset(pipelineAsset); + AZ::RPI::RPISystemInterface::Get()->SetApplicationMultisampleState(renderPipelineDescriptor->m_renderSettings.m_multisampleState); + + // Create a render pipeline from the specified asset for the window context and add the pipeline to the scene m_renderPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineAsset, *GetViewportContext()->GetWindowContext().get()); pipelineAsset.Release(); m_scene->AddRenderPipeline(m_renderPipeline); @@ -116,31 +129,24 @@ namespace MaterialEditor entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId); // Configure camera - AzFramework::EntityContextRequestBus::EventResult( - m_cameraEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "Cameraentity"); - AZ_Assert(m_cameraEntity != nullptr, "Failed to create camera entity."); + m_cameraEntity = + CreateEntity("Cameraentity", { azrtti_typeid(), azrtti_typeid() }); - // Add debug camera and controller components AZ::Debug::CameraComponentConfig cameraConfig(GetViewportContext()->GetWindowContext()); cameraConfig.m_fovY = AZ::Constants::HalfPi; cameraConfig.m_depthNear = DepthNear; - m_cameraComponent = m_cameraEntity->CreateComponent(azrtti_typeid()); - m_cameraComponent->SetConfiguration(cameraConfig); - m_cameraEntity->CreateComponent(azrtti_typeid()); + m_cameraEntity->Deactivate(); + m_cameraEntity->FindComponent(azrtti_typeid())->SetConfiguration(cameraConfig); m_cameraEntity->Activate(); // Connect camera to pipeline's default view after camera entity activated m_renderPipeline->SetDefaultViewFromEntity(m_cameraEntity->GetId()); // Configure tone mapper - AzFramework::EntityContextRequestBus::EventResult( - m_postProcessEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "postProcessEntity"); - AZ_Assert(m_postProcessEntity != nullptr, "Failed to create post process entity."); - - m_postProcessEntity->CreateComponent(AZ::Render::PostFxLayerComponentTypeId); - m_postProcessEntity->CreateComponent(AZ::Render::ExposureControlComponentTypeId); - m_postProcessEntity->CreateComponent(azrtti_typeid()); - m_postProcessEntity->Activate(); + m_postProcessEntity = CreateEntity( + "PostProcessEntity", + { AZ::Render::PostFxLayerComponentTypeId, AZ::Render::ExposureControlComponentTypeId, + azrtti_typeid() }); // Init directional light processor m_directionalLightFeatureProcessor = m_scene->GetFeatureProcessor(); @@ -154,33 +160,19 @@ namespace MaterialEditor m_skyboxFeatureProcessor->SetSkyboxMode(AZ::Render::SkyBoxMode::Cubemap); // Create IBL - AzFramework::EntityContextRequestBus::EventResult( - m_iblEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "IblEntity"); - AZ_Assert(m_iblEntity != nullptr, "Failed to create ibl entity."); - - m_iblEntity->CreateComponent(AZ::Render::ImageBasedLightComponentTypeId); - m_iblEntity->CreateComponent(azrtti_typeid()); - m_iblEntity->Activate(); + m_iblEntity = + CreateEntity("IblEntity", { AZ::Render::ImageBasedLightComponentTypeId, azrtti_typeid() }); // Create model - AzFramework::EntityContextRequestBus::EventResult( - m_modelEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportModel"); - AZ_Assert(m_modelEntity != nullptr, "Failed to create model entity."); - - m_modelEntity->CreateComponent(AZ::Render::MeshComponentTypeId); - m_modelEntity->CreateComponent(AZ::Render::MaterialComponentTypeId); - m_modelEntity->CreateComponent(azrtti_typeid()); - m_modelEntity->Activate(); + m_modelEntity = CreateEntity( + "ViewportModel", + { AZ::Render::MeshComponentTypeId, AZ::Render::MaterialComponentTypeId, azrtti_typeid() }); // Create shadow catcher - AzFramework::EntityContextRequestBus::EventResult( - m_shadowCatcherEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportShadowCatcher"); - AZ_Assert(m_shadowCatcherEntity != nullptr, "Failed to create shadow catcher entity."); - m_shadowCatcherEntity->CreateComponent(AZ::Render::MeshComponentTypeId); - m_shadowCatcherEntity->CreateComponent(AZ::Render::MaterialComponentTypeId); - m_shadowCatcherEntity->CreateComponent(azrtti_typeid()); - m_shadowCatcherEntity->CreateComponent(azrtti_typeid()); - m_shadowCatcherEntity->Activate(); + m_shadowCatcherEntity = CreateEntity( + "ViewportShadowCatcher", + { AZ::Render::MeshComponentTypeId, AZ::Render::MaterialComponentTypeId, azrtti_typeid(), + azrtti_typeid() }); AZ::NonUniformScaleRequestBus::Event( m_shadowCatcherEntity->GetId(), &AZ::NonUniformScaleRequests::SetScale, AZ::Vector3{ 100, 100, 1.0 }); @@ -213,21 +205,19 @@ namespace MaterialEditor } // Create grid - AzFramework::EntityContextRequestBus::EventResult( - m_gridEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportGrid"); - AZ_Assert(m_gridEntity != nullptr, "Failed to create grid entity."); + m_gridEntity = CreateEntity("ViewportGrid", { AZ::Render::GridComponentTypeId, azrtti_typeid() }); AZ::Render::GridComponentConfig gridConfig; gridConfig.m_gridSize = 4.0f; gridConfig.m_axisColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f); gridConfig.m_primaryColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f); gridConfig.m_secondaryColor = AZ::Color(0.1f, 0.1f, 0.1f, 1.0f); - auto gridComponent = m_gridEntity->CreateComponent(AZ::Render::GridComponentTypeId); - gridComponent->SetConfiguration(gridConfig); - - m_gridEntity->CreateComponent(azrtti_typeid()); + m_gridEntity->Deactivate(); + m_gridEntity->FindComponent(AZ::Render::GridComponentTypeId)->SetConfiguration(gridConfig); m_gridEntity->Activate(); + SetupInputController(); + OnDocumentOpened(AZ::Uuid::CreateNull()); // Attempt to apply the default lighting preset @@ -240,8 +230,6 @@ namespace MaterialEditor MaterialViewportRequestBus::BroadcastResult(modelPreset, &MaterialViewportRequestBus::Events::GetModelPresetSelection); OnModelPresetSelected(modelPreset); - m_viewportController->Init(m_cameraEntity->GetId(), m_modelEntity->GetId(), m_iblEntity->GetId()); - // Apply user settinngs restored since last run AZStd::intrusive_ptr viewportSettings = AZ::UserSettings::CreateFind(AZ::Crc32("MaterialViewportSettings"), AZ::UserSettings::CT_GLOBAL); @@ -252,12 +240,10 @@ namespace MaterialEditor OnFieldOfViewChanged(viewportSettings->m_fieldOfView); OnDisplayMapperOperationTypeChanged(viewportSettings->m_displayMapperOperationType); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect(); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId); MaterialViewportNotificationBus::Handler::BusConnect(); AZ::TickBus::Handler::BusConnect(); AZ::TransformNotificationBus::MultiHandler::BusConnect(m_cameraEntity->GetId()); - - GetControllerList()->Add(m_viewportController); } MaterialViewportWidget::~MaterialViewportWidget() @@ -268,33 +254,12 @@ namespace MaterialEditor MaterialViewportNotificationBus::Handler::BusDisconnect(); AZ::Data::AssetBus::Handler::BusDisconnect(); - AzFramework::EntityContextId entityContextId; - AzFramework::GameEntityContextRequestBus::BroadcastResult( - entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId); - - AzFramework::EntityContextRequestBus::Event( - entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_iblEntity); - m_iblEntity = nullptr; - - AzFramework::EntityContextRequestBus::Event( - entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_modelEntity); - m_modelEntity = nullptr; - - AzFramework::EntityContextRequestBus::Event( - entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_shadowCatcherEntity); - m_shadowCatcherEntity = nullptr; - - AzFramework::EntityContextRequestBus::Event( - entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_gridEntity); - m_gridEntity = nullptr; - - AzFramework::EntityContextRequestBus::Event( - entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_cameraEntity); - m_cameraEntity = nullptr; - - AzFramework::EntityContextRequestBus::Event( - entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_postProcessEntity); - m_postProcessEntity = nullptr; + DestroyEntity(m_iblEntity); + DestroyEntity(m_modelEntity); + DestroyEntity(m_shadowCatcherEntity); + DestroyEntity(m_gridEntity); + DestroyEntity(m_cameraEntity); + DestroyEntity(m_postProcessEntity); for (DirectionalLightHandle& handle : m_lightHandles) { @@ -314,6 +279,76 @@ namespace MaterialEditor m_scene = nullptr; } + AZ::Entity* MaterialViewportWidget::CreateEntity(const AZStd::string& name, const AZStd::vector& componentTypeIds) + { + AzFramework::EntityContextId entityContextId; + AzFramework::GameEntityContextRequestBus::BroadcastResult( + entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId); + + AZ::Entity* entity = {}; + AzFramework::EntityContextRequestBus::EventResult( + entity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, name.c_str()); + AZ_Assert(entity != nullptr, "Failed to create post process entity: %s.", name.c_str()); + + if (entity) + { + for (const auto& componentTypeId : componentTypeIds) + { + entity->CreateComponent(componentTypeId); + } + entity->Activate(); + } + + return entity; + } + + void MaterialViewportWidget::DestroyEntity(AZ::Entity*& entity) + { + AzFramework::EntityContextId entityContextId; + AzFramework::GameEntityContextRequestBus::BroadcastResult( + entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId); + + AzFramework::EntityContextRequestBus::Event(entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, entity); + entity = nullptr; + } + + void MaterialViewportWidget::SetupInputController() + { + using namespace AtomToolsFramework; + + // Create viewport input controller and regioster its behaviors + m_viewportController.reset( + aznew ViewportInputBehaviorController(m_cameraEntity->GetId(), m_modelEntity->GetId(), m_iblEntity->GetId())); + m_viewportController->AddBehavior( + ViewportInputBehaviorController::None, AZStd::make_shared(m_viewportController.get())); + m_viewportController->AddBehavior( + ViewportInputBehaviorController::Lmb, AZStd::make_shared(m_viewportController.get())); + m_viewportController->AddBehavior( + ViewportInputBehaviorController::Mmb, AZStd::make_shared(m_viewportController.get())); + m_viewportController->AddBehavior( + ViewportInputBehaviorController::Rmb, AZStd::make_shared(m_viewportController.get())); + m_viewportController->AddBehavior( + ViewportInputBehaviorController::Alt ^ ViewportInputBehaviorController::Lmb, + AZStd::make_shared(m_viewportController.get())); + m_viewportController->AddBehavior( + ViewportInputBehaviorController::Alt ^ ViewportInputBehaviorController::Mmb, + AZStd::make_shared(m_viewportController.get())); + m_viewportController->AddBehavior( + ViewportInputBehaviorController::Alt ^ ViewportInputBehaviorController::Rmb, + AZStd::make_shared(m_viewportController.get())); + m_viewportController->AddBehavior( + ViewportInputBehaviorController::Lmb ^ ViewportInputBehaviorController::Rmb, + AZStd::make_shared(m_viewportController.get())); + m_viewportController->AddBehavior( + ViewportInputBehaviorController::Ctrl ^ ViewportInputBehaviorController::Lmb, + AZStd::make_shared(m_viewportController.get())); + m_viewportController->AddBehavior( + ViewportInputBehaviorController::Shift ^ ViewportInputBehaviorController::Lmb, + AZStd::make_shared(m_viewportController.get())); + + GetControllerList()->Add(m_viewportController); + } + void MaterialViewportWidget::OnDocumentOpened(const AZ::Uuid& documentId) { AZ::Data::Instance materialInstance; @@ -435,8 +470,7 @@ namespace MaterialEditor void MaterialViewportWidget::OnFieldOfViewChanged(float fieldOfView) { - MaterialEditorViewportInputControllerRequestBus::Broadcast( - &MaterialEditorViewportInputControllerRequestBus::Handler::SetFieldOfView, fieldOfView); + m_viewportController->SetFieldOfView(fieldOfView); } void MaterialViewportWidget::OnDisplayMapperOperationTypeChanged(AZ::Render::DisplayMapperOperationType operationType) @@ -450,7 +484,9 @@ namespace MaterialEditor { if (m_modelAssetId == asset.GetId()) { - MaterialEditorViewportInputControllerRequestBus::Broadcast(&MaterialEditorViewportInputControllerRequestBus::Handler::Reset); + AZ::Data::Asset modelAsset = asset; + m_viewportController->SetTargetBounds(modelAsset->GetAabb()); + m_viewportController->Reset(); AZ::Data::AssetBus::Handler::BusDisconnect(asset.GetId()); } } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h index 8a660c6603..a6c7de243e 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h @@ -15,9 +15,9 @@ #include #include #include +#include #include #include -#include #include AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT @@ -57,10 +57,14 @@ namespace MaterialEditor , public AZ::TransformNotificationBus::MultiHandler { public: - MaterialViewportWidget(QWidget* parent = nullptr); + MaterialViewportWidget(const AZ::Crc32& toolId, QWidget* parent = nullptr); ~MaterialViewportWidget(); private: + AZ::Entity* CreateEntity(const AZStd::string& name, const AZStd::vector& componentTypeIds); + void DestroyEntity(AZ::Entity*& entity); + void SetupInputController(); + // AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler interface overrides... void OnDocumentOpened(const AZ::Uuid& documentId) override; @@ -84,6 +88,8 @@ namespace MaterialEditor // AZ::TransformNotificationBus::MultiHandler overrides... void OnTransformChanged(const AZ::Transform&, const AZ::Transform&) override; + const AZ::Crc32 m_toolId = {}; + using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle; AZ::Data::Instance m_swapChainPass; @@ -94,8 +100,6 @@ namespace MaterialEditor AZ::Render::DisplayMapperFeatureProcessorInterface* m_displayMapperFeatureProcessor = {}; AZ::Entity* m_cameraEntity = {}; - AZ::Component* m_cameraComponent = {}; - AZ::Entity* m_postProcessEntity = {}; AZ::Entity* m_modelEntity = {}; @@ -112,7 +116,7 @@ namespace MaterialEditor AZ::Entity* m_iblEntity = {}; AZ::Render::SkyBoxFeatureProcessorInterface* m_skyboxFeatureProcessor = {}; - AZStd::shared_ptr m_viewportController; + AZStd::shared_ptr m_viewportController; QScopedPointer m_ui; }; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorWindow.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorWindow.cpp index da742d498d..3b9aba86e8 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorWindow.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorWindow.cpp @@ -6,7 +6,6 @@ * */ -#include #include #include #include @@ -36,11 +35,9 @@ AZ_POP_DISABLE_WARNING namespace MaterialEditor { - MaterialEditorWindow::MaterialEditorWindow(QWidget* parent /* = 0 */) - : Base(parent) + MaterialEditorWindow::MaterialEditorWindow(const AZ::Crc32& toolId, QWidget* parent) + : Base(toolId, parent) { - resize(1280, 1024); - // Among other things, we need the window wrapper to save the main window size, position, and state auto mainWindowWrapper = new AzQtComponents::WindowDecorationWrapper(AzQtComponents::WindowDecorationWrapper::OptionAutoTitleBarButtons); @@ -52,36 +49,24 @@ namespace MaterialEditor QApplication::setWindowIcon(QIcon(":/Icons/materialeditor.svg")); - AZ::Name apiName = AZ::RHI::Factory::Get().GetName(); - if (!apiName.IsEmpty()) - { - QString title = QString{ "%1 (%2)" }.arg(QApplication::applicationName()).arg(apiName.GetCStr()); - setWindowTitle(title); - } - else - { - AZ_Assert(false, "Render API name not found"); - setWindowTitle(QApplication::applicationName()); - } - setObjectName("MaterialEditorWindow"); m_toolBar = new MaterialEditorToolBar(this); m_toolBar->setObjectName("ToolBar"); addToolBar(m_toolBar); - m_materialViewport = new MaterialViewportWidget(centralWidget()); + m_materialViewport = new MaterialViewportWidget(m_toolId, centralWidget()); m_materialViewport->setObjectName("Viewport"); m_materialViewport->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); centralWidget()->layout()->addWidget(m_materialViewport); m_assetBrowser->SetFilterState("", AZ::RPI::StreamingImageAsset::Group, true); m_assetBrowser->SetFilterState("", AZ::RPI::MaterialAsset::Group, true); - m_assetBrowser->SetOpenHandler([](const AZStd::string& absolutePath) { + m_assetBrowser->SetOpenHandler([this](const AZStd::string& absolutePath) { if (AzFramework::StringFunc::Path::IsExtension(absolutePath.c_str(), AZ::RPI::MaterialSourceData::Extension)) { - AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, absolutePath); + AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, absolutePath); return; } @@ -93,7 +78,7 @@ namespace MaterialEditor QDesktopServices::openUrl(QUrl::fromLocalFile(absolutePath.c_str())); }); - AddDockWidget("Inspector", new MaterialInspector, Qt::RightDockWidgetArea, Qt::Vertical); + AddDockWidget("Inspector", new MaterialInspector(m_toolId), Qt::RightDockWidgetArea, Qt::Vertical); AddDockWidget("Viewport Settings", new ViewportSettingsInspector, Qt::LeftDockWidgetArea, Qt::Vertical); SetDockWidgetVisible("Viewport Settings", false); diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorWindow.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorWindow.h index 33b60add5b..0e98e3ac84 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorWindow.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorWindow.h @@ -32,7 +32,7 @@ namespace MaterialEditor using Base = AtomToolsFramework::AtomToolsDocumentMainWindow; - MaterialEditorWindow(QWidget* parent = 0); + MaterialEditorWindow(const AZ::Crc32& toolId, QWidget* parent = 0); protected: void ResizeViewportRenderTarget(uint32_t width, uint32_t height) override; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp index e6e768a7fd..c22e65df2e 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp @@ -15,13 +15,14 @@ namespace MaterialEditor { - MaterialInspector::MaterialInspector(QWidget* parent) + MaterialInspector::MaterialInspector(const AZ::Crc32& toolId, QWidget* parent) : AtomToolsFramework::InspectorWidget(parent) + , m_toolId(toolId) { m_windowSettings = AZ::UserSettings::CreateFind( AZ::Crc32("MaterialEditorWindowSettings"), AZ::UserSettings::CT_GLOBAL); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect(); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId); } MaterialInspector::~MaterialInspector() diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h index fe688e3ad5..901affd1b1 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h @@ -29,7 +29,7 @@ namespace MaterialEditor public: AZ_CLASS_ALLOCATOR(MaterialInspector, AZ::SystemAllocator, 0); - explicit MaterialInspector(QWidget* parent = nullptr); + MaterialInspector(const AZ::Crc32& toolId, QWidget* parent = nullptr); ~MaterialInspector() override; // AtomToolsFramework::InspectorRequestBus::Handler overrides... @@ -59,6 +59,8 @@ namespace MaterialEditor void RequestPropertyContextMenu([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode, const QPoint&) override {} void PropertySelectionChanged([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode, bool) override {} + const AZ::Crc32 m_toolId = {}; + // Tracking the property that is activiley being edited in the inspector const AtomToolsFramework::DynamicProperty* m_activeProperty = {}; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake b/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake index d68e013c59..dc8595fe1e 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake +++ b/Gems/Atom/Tools/MaterialEditor/Code/materialeditor_files.cmake @@ -17,28 +17,9 @@ set(FILES Source/Viewport/MaterialViewportModule.h Source/Viewport/MaterialViewportModule.cpp - Source/Viewport/InputController/MaterialEditorViewportInputControllerBus.h Source/Viewport/MaterialViewportSettings.h Source/Viewport/MaterialViewportRequestBus.h Source/Viewport/MaterialViewportNotificationBus.h - Source/Viewport/InputController/MaterialEditorViewportInputController.cpp - Source/Viewport/InputController/MaterialEditorViewportInputController.h - Source/Viewport/InputController/Behavior.cpp - Source/Viewport/InputController/Behavior.h - Source/Viewport/InputController/DollyCameraBehavior.cpp - Source/Viewport/InputController/DollyCameraBehavior.h - Source/Viewport/InputController/IdleBehavior.cpp - Source/Viewport/InputController/IdleBehavior.h - Source/Viewport/InputController/MoveCameraBehavior.cpp - Source/Viewport/InputController/MoveCameraBehavior.h - Source/Viewport/InputController/PanCameraBehavior.cpp - Source/Viewport/InputController/PanCameraBehavior.h - Source/Viewport/InputController/OrbitCameraBehavior.cpp - Source/Viewport/InputController/OrbitCameraBehavior.h - Source/Viewport/InputController/RotateEnvironmentBehavior.cpp - Source/Viewport/InputController/RotateEnvironmentBehavior.h - Source/Viewport/InputController/RotateModelBehavior.cpp - Source/Viewport/InputController/RotateModelBehavior.h Source/Viewport/MaterialViewportSettings.cpp Source/Viewport/MaterialViewportComponent.cpp Source/Viewport/MaterialViewportComponent.h diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.cpp index 5698e33cec..94e203be26 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.cpp +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.cpp @@ -6,16 +6,51 @@ * */ +#include +#include #include +#include +#include #include +#include #include +#include +#include +#include +#include #include +#include +#include #include namespace ShaderManagementConsole { - ShaderManagementConsoleDocument::ShaderManagementConsoleDocument() - : AtomToolsFramework::AtomToolsDocument() + void ShaderManagementConsoleDocument::Reflect(AZ::ReflectContext* context) + { + if (auto serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(0); + } + + if (auto behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("ShaderManagementConsoleDocumentRequestBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "Editor") + ->Attribute(AZ::Script::Attributes::Module, "shadermanagementconsole") + ->Event("SetShaderVariantListSourceData", &ShaderManagementConsoleDocumentRequestBus::Events::SetShaderVariantListSourceData) + ->Event("GetShaderVariantListSourceData", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantListSourceData) + ->Event("GetShaderOptionCount", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionCount) + ->Event("GetShaderOptionDescriptor", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptor) + ->Event("GetShaderVariantCount", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantCount) + ->Event("GetShaderVariantInfo", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantInfo) + ; + } + } + + ShaderManagementConsoleDocument::ShaderManagementConsoleDocument(const AZ::Crc32& toolId) + : AtomToolsFramework::AtomToolsDocument(toolId) { ShaderManagementConsoleDocumentRequestBus::Handler::BusConnect(m_id); } @@ -25,20 +60,18 @@ namespace ShaderManagementConsole ShaderManagementConsoleDocumentRequestBus::Handler::BusDisconnect(); } - void ShaderManagementConsoleDocument::SetShaderVariantListSourceData(const AZ::RPI::ShaderVariantListSourceData& sourceData) + void ShaderManagementConsoleDocument::SetShaderVariantListSourceData( + const AZ::RPI::ShaderVariantListSourceData& shaderVariantListSourceData) { - m_shaderVariantListSourceData = sourceData; + m_shaderVariantListSourceData = shaderVariantListSourceData; AZStd::string shaderPath = m_shaderVariantListSourceData.m_shaderFilePath; AzFramework::StringFunc::Path::ReplaceExtension(shaderPath, AZ::RPI::ShaderAsset::Extension); m_shaderAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath(shaderPath.c_str()); - if (!m_shaderAsset) - { - AZ_Error("ShaderManagementConsoleDocument", false, "Could not load shader asset: %s.", shaderPath.c_str()); - } + AZ_Error("ShaderManagementConsoleDocument", m_shaderAsset.IsReady(), "Could not load shader asset: %s.", shaderPath.c_str()); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentModified, m_id); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentModified, m_id); } const AZ::RPI::ShaderVariantListSourceData& ShaderManagementConsoleDocument::GetShaderVariantListSourceData() const @@ -107,22 +140,18 @@ namespace ShaderManagementConsole return false; } - if (!AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), AZ::RPI::ShaderVariantListSourceData::Extension)) + if (AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), AZ::RPI::ShaderSourceData::Extension)) { - AZ_Error("ShaderManagementConsoleDocument", false, "Document extension is not supported: '%s.'", m_absolutePath.c_str()); - return OpenFailed(); + return LoadShaderSourceData(); } - // Load the shader config data and create a shader config asset from it - AZ::RPI::ShaderVariantListSourceData sourceData; - if (!AZ::RPI::JsonUtils::LoadObjectFromFile(m_absolutePath, sourceData)) + if (AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), AZ::RPI::ShaderVariantListSourceData::Extension)) { - AZ_Error("ShaderManagementConsoleDocument", false, "Failed loading shader variant list data: '%s.'", m_absolutePath.c_str()); - return OpenFailed(); + return LoadShaderVariantListSourceData(); } - SetShaderVariantListSourceData(sourceData); - return IsOpen() ? OpenSucceeded() : OpenFailed(); + AZ_Error("ShaderManagementConsoleDocument", false, "Document extension is not supported: '%s.'", m_absolutePath.c_str()); + return OpenFailed(); } bool ShaderManagementConsoleDocument::Save() @@ -173,7 +202,8 @@ namespace ShaderManagementConsole bool ShaderManagementConsoleDocument::IsSavable() const { - return true; + return IsOpen() && + AzFramework::StringFunc::Path::IsExtension(m_absolutePath.c_str(), AZ::RPI::ShaderVariantListSourceData::Extension); } void ShaderManagementConsoleDocument::Clear() @@ -195,4 +225,178 @@ namespace ShaderManagementConsole m_absolutePath = m_savePathNormalized; return SaveSucceeded(); } + + bool ShaderManagementConsoleDocument::LoadShaderSourceData() + { + // Get info such as relative path of the file and asset id + bool result = false; + AZ::Data::AssetInfo shaderAssetInfo; + AZStd::string watchFolder; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, m_absolutePath.c_str(), shaderAssetInfo, + watchFolder); + + if (!result) + { + AZ_Error("ShaderManagementConsoleDocument", false, "Failed to get the asset info for the file: %s.", m_absolutePath.c_str()); + return OpenFailed(); + } + + // retrieves a list of all material source files that use the shader. Note that materials inherit from materialtype files, which + // are actual files that refer to shader files. + const auto& materialAssetIds = FindMaterialAssetsUsingShader(shaderAssetInfo.m_relativePath); + + // This loop collects all uniquely-identified shader items used by the materials based on its shader variant id. + AZStd::set shaderVariantIds; + AZStd::vector shaderVariantListShaderOptionGroups; + for (const auto& materialAssetId : materialAssetIds) + { + const auto& materialInstanceShaderItems = GetMaterialInstanceShaderItems(materialAssetId); + for (const auto& shaderItem : materialInstanceShaderItems) + { + const auto& shaderAssetId = shaderItem.GetShaderAsset().GetId(); + if (shaderAssetInfo.m_assetId == shaderAssetId) + { + const auto& shaderVariantId = shaderItem.GetShaderVariantId(); + if (!shaderVariantId.IsEmpty() && shaderVariantIds.insert(shaderVariantId).second) + { + shaderVariantListShaderOptionGroups.push_back(shaderItem.GetShaderOptionGroup()); + } + } + } + } + + // Generate the shader variant list data by collecting shader option name-value pairs.s + AZ::RPI::ShaderVariantListSourceData shaderVariantListSourceData; + shaderVariantListSourceData.m_shaderFilePath = shaderAssetInfo.m_relativePath; + int stableId = 1; + for (const auto& shaderOptionGroup : shaderVariantListShaderOptionGroups) + { + AZ::RPI::ShaderVariantListSourceData::VariantInfo variantInfo; + variantInfo.m_stableId = stableId; + + const auto& shaderOptionDescriptors = shaderOptionGroup.GetShaderOptionDescriptors(); + for (const auto& shaderOptionDescriptor : shaderOptionDescriptors) + { + const auto& optionName = shaderOptionDescriptor.GetName(); + const auto& optionValue = shaderOptionGroup.GetValue(optionName); + if (!optionValue.IsValid()) + { + continue; + } + + const auto& valueName = shaderOptionDescriptor.GetValueName(optionValue); + variantInfo.m_options[optionName.GetStringView()] = valueName.GetStringView(); + } + + if (!variantInfo.m_options.empty()) + { + shaderVariantListSourceData.m_shaderVariants.push_back(variantInfo); + stableId++; + } + } + + SetShaderVariantListSourceData(shaderVariantListSourceData); + return IsOpen() ? OpenSucceeded() : OpenFailed(); + } + + bool ShaderManagementConsoleDocument::LoadShaderVariantListSourceData() + { + // Load previously generated shader variant list source data + AZ::RPI::ShaderVariantListSourceData shaderVariantListSourceData; + if (!AZ::RPI::JsonUtils::LoadObjectFromFile(m_absolutePath, shaderVariantListSourceData)) + { + AZ_Error("ShaderManagementConsoleDocument", false, "Failed loading shader variant list data: '%s.'", m_absolutePath.c_str()); + return OpenFailed(); + } + + SetShaderVariantListSourceData(shaderVariantListSourceData); + return IsOpen() ? OpenSucceeded() : OpenFailed(); + } + + AZStd::vector ShaderManagementConsoleDocument::FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath) + { + AzToolsFramework::AssetDatabase::AssetDatabaseConnection assetDatabaseConnection; + assetDatabaseConnection.OpenDatabase(); + + // Find all material types that reference shaderFilePath + AZStd::vector materialTypeSources; + + assetDatabaseConnection.QuerySourceDependencyByDependsOnSource( + shaderFilePath.c_str(), nullptr, AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::DEP_Any, + [&](AzToolsFramework::AssetDatabase::SourceFileDependencyEntry& sourceFileDependencyEntry) + { + if (AzFramework::StringFunc::Path::IsExtension( + sourceFileDependencyEntry.m_source.c_str(), AZ::RPI::MaterialTypeSourceData::Extension)) + { + materialTypeSources.push_back(sourceFileDependencyEntry.m_source); + } + return true; + }); + + // Find all materials that reference any of the material types using this shader + AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer productDependencies; + for (const auto& materialTypeSource : materialTypeSources) + { + bool result = false; + AZ::Data::AssetInfo materialTypeSourceAssetInfo; + AZStd::string watchFolder; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult( + result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, materialTypeSource.c_str(), + materialTypeSourceAssetInfo, watchFolder); + + assetDatabaseConnection.QueryDirectReverseProductDependenciesBySourceGuidSubId( + materialTypeSourceAssetInfo.m_assetId.m_guid, materialTypeSourceAssetInfo.m_assetId.m_subId, + [&](AzToolsFramework::AssetDatabase::ProductDatabaseEntry& entry) + { + if (AzFramework::StringFunc::Path::IsExtension(entry.m_productName.c_str(), AZ::RPI::MaterialAsset::Extension)) + { + productDependencies.push_back(entry); + } + return true; + }); + } + + AZStd::vector results; + results.reserve(productDependencies.size()); + for (auto product : productDependencies) + { + assetDatabaseConnection.QueryCombinedByProductID( + product.m_productID, + [&](AzToolsFramework::AssetDatabase::CombinedDatabaseEntry& combined) + { + results.push_back({ combined.m_sourceGuid, combined.m_subID }); + return false; + }, + nullptr); + } + + return results; + } + + AZStd::vector ShaderManagementConsoleDocument::GetMaterialInstanceShaderItems( + const AZ::Data::AssetId& materialAssetId) + { + auto materialAsset = + AZ::RPI::AssetUtils::LoadAssetById(materialAssetId, AZ::RPI::AssetUtils::TraceLevel::Error); + if (!materialAsset.IsReady()) + { + AZ_Error( + "ShaderManagementConsoleDocument", false, "Failed to load material asset from asset id: %s", + materialAssetId.ToString().c_str()); + return AZStd::vector(); + } + + auto materialInstance = AZ::RPI::Material::Create(materialAsset); + if (!materialInstance) + { + AZ_Error( + "ShaderManagementConsoleDocument", false, "Failed to create material instance from asset: %s", + materialAsset.ToString().c_str()); + return AZStd::vector(); + } + + return AZStd::vector( + materialInstance->GetShaderCollection().begin(), materialInstance->GetShaderCollection().end()); + } } // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.h index 5c3d66b0a9..3a1e6b8ddd 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.h @@ -5,30 +5,34 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once #include +#include #include #include #include #include +#include #include namespace ShaderManagementConsole { - /** - * ShaderManagementConsoleDocument provides an API for modifying and saving document properties. - */ + //! ShaderManagementConsoleDocument provides an API for modifying and saving document properties. class ShaderManagementConsoleDocument : public AtomToolsFramework::AtomToolsDocument , public ShaderManagementConsoleDocumentRequestBus::Handler { public: - AZ_RTTI(ShaderManagementConsoleDocument, "{DBA269AE-892B-415C-8FA1-166B94B0E045}"); + AZ_RTTI(ShaderManagementConsoleDocument, "{C8FAF1C7-8665-423C-B1DD-82016231B17B}", AtomToolsFramework::AtomToolsDocument); AZ_CLASS_ALLOCATOR(ShaderManagementConsoleDocument, AZ::SystemAllocator, 0); - AZ_DISABLE_COPY(ShaderManagementConsoleDocument); + AZ_DISABLE_COPY_MOVE(ShaderManagementConsoleDocument); + + static void Reflect(AZ::ReflectContext* context); - ShaderManagementConsoleDocument(); + ShaderManagementConsoleDocument() = default; + ShaderManagementConsoleDocument(const AZ::Crc32& toolId); ~ShaderManagementConsoleDocument(); // AtomToolsFramework::AtomToolsDocument overrides... @@ -42,7 +46,7 @@ namespace ShaderManagementConsole bool IsSavable() const override; // ShaderManagementConsoleDocumentRequestBus::Handler overridfes... - void SetShaderVariantListSourceData(const AZ::RPI::ShaderVariantListSourceData& sourceData) override; + void SetShaderVariantListSourceData(const AZ::RPI::ShaderVariantListSourceData& shaderVariantListSourceData) override; const AZ::RPI::ShaderVariantListSourceData& GetShaderVariantListSourceData() const override; size_t GetShaderVariantCount() const override; const AZ::RPI::ShaderVariantListSourceData::VariantInfo& GetShaderVariantInfo(size_t index) const override; @@ -53,8 +57,21 @@ namespace ShaderManagementConsole // AtomToolsFramework::AtomToolsDocument overrides... void Clear() override; + // Write shader variant list source data to JSON bool SaveSourceData(); + // Read shader variant list source data from JSON and initialize the document + bool LoadShaderSourceData(); + + // Read shader source data from JSON then find all references to to populate the shader variant list and initialize the document + bool LoadShaderVariantListSourceData(); + + // Find all material assets that reference material types using shaderFilePath + AZStd::vector FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath); + + // Retrieve all of the shader collection items from a material instance created from materialAssetId + AZStd::vector GetMaterialInstanceShaderItems(const AZ::Data::AssetId& materialAssetId); + // Source data for shader variant list AZ::RPI::ShaderVariantListSourceData m_shaderVariantListSourceData; diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentRequestBus.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentRequestBus.h index e58ee34ed1..e249146af6 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentRequestBus.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocumentRequestBus.h @@ -5,6 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ + #pragma once #include @@ -14,9 +15,7 @@ namespace ShaderManagementConsole { - - class ShaderManagementConsoleDocumentRequests - : public AZ::EBusTraits + class ShaderManagementConsoleDocumentRequests : public AZ::EBusTraits { public: static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; @@ -24,7 +23,7 @@ namespace ShaderManagementConsole typedef AZ::Uuid BusIdType; //! Set the shader variant list - virtual void SetShaderVariantListSourceData(const AZ::RPI::ShaderVariantListSourceData& sourceData) = 0; + virtual void SetShaderVariantListSourceData(const AZ::RPI::ShaderVariantListSourceData& shaderVariantListSourceData) = 0; //! Get the shader variant list virtual const AZ::RPI::ShaderVariantListSourceData& GetShaderVariantListSourceData() const = 0; diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp index 2d61730c4d..ce3a390a22 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp @@ -6,27 +6,17 @@ * */ -#include -#include -#include -#include -#include #include -#include #include #include #include #include #include -#include #include -#include #include -#include #include #include #include -#include #include #include @@ -47,57 +37,34 @@ void InitShaderManagementConsoleResources() namespace ShaderManagementConsole { + static const char* GetBuildTargetName() + { +#if !defined(LY_CMAKE_TARGET) +#error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target" +#endif + return LY_CMAKE_TARGET; + } + ShaderManagementConsoleApplication::ShaderManagementConsoleApplication(int* argc, char*** argv) - : Base(argc, argv) + : Base(GetBuildTargetName(), argc, argv) { InitShaderManagementConsoleResources(); QApplication::setApplicationName("O3DE Shader Management Console"); - // The settings registry has been created at this point, so add the CMake target - AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization( - *AZ::SettingsRegistry::Get(), GetBuildTargetName()); - - ShaderManagementConsoleRequestBus::Handler::BusConnect(); AzToolsFramework::EditorWindowRequestBus::Handler::BusConnect(); - AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusConnect(); } ShaderManagementConsoleApplication::~ShaderManagementConsoleApplication() { - ShaderManagementConsoleRequestBus::Handler::BusDisconnect(); AzToolsFramework::EditorWindowRequestBus::Handler::BusDisconnect(); - AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusDisconnect(); m_window.reset(); } void ShaderManagementConsoleApplication::Reflect(AZ::ReflectContext* context) { Base::Reflect(context); - - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->EBus("ShaderManagementConsoleRequestBus") - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) - ->Attribute(AZ::Script::Attributes::Category, "Editor") - ->Attribute(AZ::Script::Attributes::Module, "shadermanagementconsole") - ->Event("GetSourceAssetInfo", &ShaderManagementConsoleRequestBus::Events::GetSourceAssetInfo) - ->Event("FindMaterialAssetsUsingShader", &ShaderManagementConsoleRequestBus::Events::FindMaterialAssetsUsingShader ) - ->Event("GetMaterialInstanceShaderItems", &ShaderManagementConsoleRequestBus::Events::GetMaterialInstanceShaderItems) - ; - - behaviorContext->EBus("ShaderManagementConsoleDocumentRequestBus") - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) - ->Attribute(AZ::Script::Attributes::Category, "Editor") - ->Attribute(AZ::Script::Attributes::Module, "shadermanagementconsole") - ->Event("SetShaderVariantListSourceData", &ShaderManagementConsoleDocumentRequestBus::Events::SetShaderVariantListSourceData) - ->Event("GetShaderVariantListSourceData", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantListSourceData) - ->Event("GetShaderOptionCount", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionCount) - ->Event("GetShaderOptionDescriptor", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptor) - ->Event("GetShaderVariantCount", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantCount) - ->Event("GetShaderVariantInfo", &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantInfo) - ; - } + ShaderManagementConsoleDocument::Reflect(context); } const char* ShaderManagementConsoleApplication::GetCurrentConfigurationName() const @@ -115,40 +82,20 @@ namespace ShaderManagementConsole { Base::StartCommon(systemEntity); - AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Handler::RegisterDocumentType, - []() { return aznew ShaderManagementConsoleDocument(); }); - } - - AZStd::string ShaderManagementConsoleApplication::GetBuildTargetName() const - { -#if !defined(LY_CMAKE_TARGET) -#error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target" -#endif - //! Returns the build system target name of "ShaderManagementConsole" - return AZStd::string_view{ LY_CMAKE_TARGET }; - } - - AZStd::vector ShaderManagementConsoleApplication::GetCriticalAssetFilters() const - { - return AZStd::vector({ "passes/", "config/" }); - } + AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Handler::RegisterDocumentType, + [](const AZ::Crc32& toolId) { return aznew ShaderManagementConsoleDocument(toolId); }); - QWidget* ShaderManagementConsoleApplication::GetAppMainWindow() - { - return m_window.get(); - } + m_window.reset(aznew ShaderManagementConsoleWindow(m_toolId)); - void ShaderManagementConsoleApplication::CreateMainWindow() - { - m_window.reset(aznew ShaderManagementConsoleWindow); m_assetBrowserInteractions.reset(aznew AtomToolsFramework::AtomToolsAssetBrowserInteractions); + m_assetBrowserInteractions->RegisterContextMenuActions( [](const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries) { return entries.front()->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Source; }, - []([[maybe_unused]] QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries) + [this]([[maybe_unused]] QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries) { if (AzFramework::StringFunc::Path::IsExtension( entries.front()->GetFullPath().c_str(), AZ::RPI::ShaderSourceData::Extension)) @@ -163,13 +110,16 @@ namespace ShaderManagementConsole pythonArgs); }); } - else if (AzFramework::StringFunc::Path::IsExtension( - entries.front()->GetFullPath().c_str(), AZ::RPI::ShaderVariantListSourceData::Extension)) + + if (AzFramework::StringFunc::Path::IsExtension( + entries.front()->GetFullPath().c_str(), AZ::RPI::ShaderSourceData::Extension) || + AzFramework::StringFunc::Path::IsExtension( + entries.front()->GetFullPath().c_str(), AZ::RPI::ShaderVariantListSourceData::Extension)) { - menu->addAction(QObject::tr("Open"), [entries]() + menu->addAction(QObject::tr("Open"), [entries, this]() { - AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, + AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, entries.front()->GetFullPath()); }); } @@ -183,104 +133,19 @@ namespace ShaderManagementConsole }); } - void ShaderManagementConsoleApplication::DestroyMainWindow() + void ShaderManagementConsoleApplication::Destroy() { m_window.reset(); + Base::Destroy(); } - AZ::Data::AssetInfo ShaderManagementConsoleApplication::GetSourceAssetInfo(const AZStd::string& sourceAssetFileName) - { - bool result = false; - AZ::Data::AssetInfo assetInfo; - AZStd::string watchFolder; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult( - result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, sourceAssetFileName.c_str(), assetInfo, - watchFolder); - AZ_Error(nullptr, result, "Failed to get the asset info for the file: %s.", sourceAssetFileName.c_str()); - - return assetInfo; - } - - AZStd::vector ShaderManagementConsoleApplication::FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath) + AZStd::vector ShaderManagementConsoleApplication::GetCriticalAssetFilters() const { - // Collect the material types referencing the shader - AZStd::vector materialTypeSources; - - AzToolsFramework::AssetDatabase::AssetDatabaseConnection assetDatabaseConnection; - assetDatabaseConnection.OpenDatabase(); - - assetDatabaseConnection.QuerySourceDependencyByDependsOnSource( - shaderFilePath.c_str(), nullptr, AzToolsFramework::AssetDatabase::SourceFileDependencyEntry::DEP_Any, - [&](AzToolsFramework::AssetDatabase::SourceFileDependencyEntry& sourceFileDependencyEntry) - { - AZStd::string assetExtension; - if (AzFramework::StringFunc::Path::GetExtension(sourceFileDependencyEntry.m_source.c_str(), assetExtension, false)) - { - if (assetExtension == "materialtype") - { - materialTypeSources.push_back(sourceFileDependencyEntry.m_source); - } - } - return true; - }); - - AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer productDependencies; - for (const auto& materialTypeSource : materialTypeSources) - { - bool result = false; - AZ::Data::AssetInfo materialTypeSourceAssetInfo; - AZStd::string watchFolder; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult( - result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, materialTypeSource.c_str(), - materialTypeSourceAssetInfo, watchFolder); - - assetDatabaseConnection.QueryDirectReverseProductDependenciesBySourceGuidSubId( - materialTypeSourceAssetInfo.m_assetId.m_guid, materialTypeSourceAssetInfo.m_assetId.m_subId, - [&](AzToolsFramework::AssetDatabase::ProductDatabaseEntry& entry) - { - AZStd::string assetExtension; - if (AzFramework::StringFunc::Path::GetExtension(entry.m_productName.c_str(), assetExtension, false)) - { - if (assetExtension == "azmaterial") - { - productDependencies.push_back(entry); - } - } - return true; - }); - } - - AZStd::vector results; - results.reserve(productDependencies.size()); - for (auto product : productDependencies) - { - assetDatabaseConnection.QueryCombinedByProductID( - product.m_productID, - [&](AzToolsFramework::AssetDatabase::CombinedDatabaseEntry& combined) - { - results.push_back({ combined.m_sourceGuid, combined.m_subID }); - return false; - }, - nullptr); - } - return results; + return AZStd::vector({ "passes/", "config/" }); } - AZStd::vector ShaderManagementConsoleApplication::GetMaterialInstanceShaderItems( - const AZ::Data::AssetId& assetId) + QWidget* ShaderManagementConsoleApplication::GetAppMainWindow() { - auto materialAsset = AZ::RPI::AssetUtils::LoadAssetById(assetId, AZ::RPI::AssetUtils::TraceLevel::Error); - - auto materialInstance = AZ::RPI::Material::Create(materialAsset); - AZ_Error( - nullptr, materialAsset, "Failed to get a material instance from product asset id: %s", - assetId.ToString().c_str()); - - if (materialInstance != nullptr) - { - return AZStd::vector( - materialInstance->GetShaderCollection().begin(), materialInstance->GetShaderCollection().end()); - } - return AZStd::vector(); + return m_window.get(); } } // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h index 232f204790..01d0b9e415 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h @@ -11,17 +11,13 @@ #include #include #include -#include #include -#include #include namespace ShaderManagementConsole { class ShaderManagementConsoleApplication : public AtomToolsFramework::AtomToolsDocumentApplication - , private ShaderManagementConsoleRequestBus::Handler - , private AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler , private AzToolsFramework::EditorWindowRequestBus::Handler { public: @@ -36,23 +32,14 @@ namespace ShaderManagementConsole void Reflect(AZ::ReflectContext* context) override; const char* GetCurrentConfigurationName() const override; void StartCommon(AZ::Entity* systemEntity) override; + void Destroy() override; // AtomToolsFramework::AtomToolsApplication overrides... - AZStd::string GetBuildTargetName() const override; AZStd::vector GetCriticalAssetFilters() const override; // AzToolsFramework::EditorWindowRequests::Bus::Handler QWidget* GetAppMainWindow() override; - // AtomToolsMainWindowFactoryRequestBus::Handler overrides... - void CreateMainWindow() override; - void DestroyMainWindow() override; - - // ShaderManagementConsoleRequestBus::Handler overrides... - AZ::Data::AssetInfo GetSourceAssetInfo(const AZStd::string& sourceAssetFileName) override; - AZStd::vector FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath) override; - AZStd::vector GetMaterialInstanceShaderItems(const AZ::Data::AssetId& assetId) override; - private: AZStd::unique_ptr m_window; AZStd::unique_ptr m_assetBrowserInteractions; diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleRequestBus.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleRequestBus.h deleted file mode 100644 index 355c51ed54..0000000000 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleRequestBus.h +++ /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 - * - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace ShaderManagementConsole -{ - //! ShaderManagementConsoleRequestBus provides - class ShaderManagementConsoleRequests - : public AZ::EBusTraits - { - public: - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; - - //! Returns a shader file's asset id and relative filepath - virtual AZ::Data::AssetInfo GetSourceAssetInfo(const AZStd::string& sourceAssetFileName) = 0; - - // [GFX TODO][ATOM-14857] Generalize this API - //! Returns a list of material AssetIds that use the shader file. - virtual AZStd::vector FindMaterialAssetsUsingShader (const AZStd::string& shaderFilePath) = 0; - - //! Returns a list of shader items contained within an instantiated material source's shader collection. - virtual AZStd::vector GetMaterialInstanceShaderItems(const AZ::Data::AssetId& assetId) = 0; - }; - using ShaderManagementConsoleRequestBus = AZ::EBus; -} // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleTableView.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleTableView.cpp new file mode 100644 index 0000000000..12445f7fa3 --- /dev/null +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleTableView.cpp @@ -0,0 +1,106 @@ +/* + * 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 ShaderManagementConsole +{ + ShaderManagementConsoleTableView::ShaderManagementConsoleTableView( + const AZ::Crc32& toolId, const AZ::Uuid& documentId, QWidget* parent) + : QTableView(parent) + , m_toolId(toolId) + , m_documentId(documentId) + , m_model(new QStandardItemModel(this)) + { + setSelectionBehavior(QAbstractItemView::SelectRows); + setModel(m_model); + + RebuildTable(); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId); + } + + ShaderManagementConsoleTableView::~ShaderManagementConsoleTableView() + { + AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect(); + } + + void ShaderManagementConsoleTableView::OnDocumentOpened(const AZ::Uuid& documentId) + { + if (m_documentId == documentId) + { + RebuildTable(); + } + } + + void ShaderManagementConsoleTableView::OnDocumentModified(const AZ::Uuid& documentId) + { + if (m_documentId == documentId) + { + RebuildTable(); + } + } + + void ShaderManagementConsole::ShaderManagementConsoleTableView::RebuildTable() + { + AZStd::unordered_set optionNames; + + size_t shaderOptionCount = 0; + ShaderManagementConsoleDocumentRequestBus::EventResult( + shaderOptionCount, m_documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionCount); + + for (size_t optionIndex = 0; optionIndex < shaderOptionCount; ++optionIndex) + { + AZ::RPI::ShaderOptionDescriptor shaderOptionDesc; + ShaderManagementConsoleDocumentRequestBus::EventResult( + shaderOptionDesc, m_documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptor, optionIndex); + optionNames.insert(shaderOptionDesc.GetName().GetCStr()); + } + + size_t shaderVariantCount = 0; + ShaderManagementConsoleDocumentRequestBus::EventResult( + shaderVariantCount, m_documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantCount); + + m_model->clear(); + m_model->setRowCount(static_cast(shaderVariantCount)); + m_model->setColumnCount(static_cast(optionNames.size())); + + int nameIndex = 0; + for (const auto& optionName : optionNames) + { + m_model->setHeaderData(nameIndex++, Qt::Horizontal, optionName.c_str()); + } + + for (int variantIndex = 0; variantIndex < shaderVariantCount; ++variantIndex) + { + AZ::RPI::ShaderVariantListSourceData::VariantInfo shaderVariantInfo; + ShaderManagementConsoleDocumentRequestBus::EventResult( + shaderVariantInfo, m_documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantInfo, variantIndex); + + m_model->setHeaderData(variantIndex, Qt::Vertical, QString::number(variantIndex)); + + for (const auto& shaderOption : shaderVariantInfo.m_options) + { + AZ::Name optionName{ shaderOption.first }; + AZ::Name optionValue{ shaderOption.second }; + + auto optionIt = optionNames.find(optionName.GetCStr()); + int optionIndex = static_cast(AZStd::distance(optionNames.begin(), optionIt)); + + QStandardItem* item = new QStandardItem(optionValue.GetCStr()); + m_model->setItem(variantIndex, optionIndex, item); + } + } + } +} // namespace ShaderManagementConsole + +#include +#include "ShaderManagementConsoleTableView.h" diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleTableView.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleTableView.h new file mode 100644 index 0000000000..1d62c97412 --- /dev/null +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleTableView.h @@ -0,0 +1,40 @@ +/* + * 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 + +#if !defined(Q_MOC_RUN) +#include + +#include +#include +#endif + +namespace ShaderManagementConsole +{ + class ShaderManagementConsoleTableView + : public QTableView + , public AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler + { + public: + AZ_CLASS_ALLOCATOR(ShaderManagementConsoleTableView, AZ::SystemAllocator, 0); + ShaderManagementConsoleTableView(const AZ::Crc32& toolId, const AZ::Uuid& documentId, QWidget* parent); + ~ShaderManagementConsoleTableView(); + + protected: + // AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler overrides... + void OnDocumentOpened(const AZ::Uuid& documentId) override; + void OnDocumentModified(const AZ::Uuid& documentId) override; + + void RebuildTable(); + + const AZ::Crc32 m_toolId = {}; + const AZ::Uuid m_documentId = AZ::Uuid::CreateNull(); + QStandardItemModel* m_model = {}; + }; +} // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp index 04a8a71191..79e9804f87 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp @@ -6,31 +6,25 @@ * */ +#include +#include +#include #include -#include -#include +#include #include -#include +#include #include -#include -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT #include #include -#include -#include -#include #include #include -AZ_POP_DISABLE_WARNING namespace ShaderManagementConsole { - ShaderManagementConsoleWindow::ShaderManagementConsoleWindow(QWidget* parent /* = 0 */) - : Base(parent) + ShaderManagementConsoleWindow::ShaderManagementConsoleWindow(const AZ::Crc32& toolId, QWidget* parent) + : Base(toolId, parent) { - resize(1280, 1024); - // Among other things, we need the window wrapper to save the main window size, position, and state auto mainWindowWrapper = new AzQtComponents::WindowDecorationWrapper(AzQtComponents::WindowDecorationWrapper::OptionAutoTitleBarButtons); @@ -42,16 +36,18 @@ namespace ShaderManagementConsole setObjectName("ShaderManagementConsoleWindow"); m_assetBrowser->SetFilterState("", AZ::RPI::ShaderAsset::Group, true); - m_assetBrowser->SetOpenHandler([](const AZStd::string& absolutePath) { - if (AzFramework::StringFunc::Path::IsExtension(absolutePath.c_str(), AZ::RPI::ShaderVariantListSourceData::Extension)) + m_assetBrowser->SetOpenHandler([this](const AZStd::string& absolutePath) { - AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast( - &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, absolutePath); - return; - } + if (AzFramework::StringFunc::Path::IsExtension(absolutePath.c_str(), AZ::RPI::ShaderSourceData::Extension) || + AzFramework::StringFunc::Path::IsExtension(absolutePath.c_str(), AZ::RPI::ShaderVariantListSourceData::Extension)) + { + AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event( + m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, absolutePath); + return; + } - QDesktopServices::openUrl(QUrl::fromLocalFile(absolutePath.c_str())); - }); + QDesktopServices::openUrl(QUrl::fromLocalFile(absolutePath.c_str())); + }); // Restore geometry and show the window mainWindowWrapper->showFromSettings(); @@ -68,68 +64,18 @@ namespace ShaderManagementConsole bool ShaderManagementConsoleWindow::GetOpenDocumentParams(AZStd::string& openPath) { openPath = QFileDialog::getOpenFileName( - this, tr("Open Document"), AZ::Utils::GetProjectPath().c_str(), tr("Files (*.%1)").arg(AZ::RPI::ShaderVariantListSourceData::Extension)).toUtf8().constData(); + this, tr("Open Document"), AZ::Utils::GetProjectPath().c_str(), + tr("Shader Files (*.%1);;Shader Variant List Files (*.%2)") + .arg(AZ::RPI::ShaderSourceData::Extension) + .arg(AZ::RPI::ShaderVariantListSourceData::Extension)) + .toUtf8() + .constData(); return !openPath.empty(); } QWidget* ShaderManagementConsoleWindow::CreateDocumentTabView(const AZ::Uuid& documentId) { - AZStd::unordered_set optionNames; - - size_t shaderOptionCount = 0; - ShaderManagementConsoleDocumentRequestBus::EventResult( - shaderOptionCount, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionCount); - - for (size_t optionIndex = 0; optionIndex < shaderOptionCount; ++optionIndex) - { - AZ::RPI::ShaderOptionDescriptor shaderOptionDesc; - ShaderManagementConsoleDocumentRequestBus::EventResult( - shaderOptionDesc, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderOptionDescriptor, optionIndex); - - const char* optionName = shaderOptionDesc.GetName().GetCStr(); - optionNames.insert(optionName); - } - - size_t shaderVariantCount = 0; - ShaderManagementConsoleDocumentRequestBus::EventResult( - shaderVariantCount, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantCount); - - auto model = new QStandardItemModel(); - model->setRowCount(static_cast(shaderVariantCount)); - model->setColumnCount(static_cast(optionNames.size())); - - int nameIndex = 0; - for (const auto& optionName : optionNames) - { - model->setHeaderData(nameIndex++, Qt::Horizontal, optionName.c_str()); - } - - for (int variantIndex = 0; variantIndex < shaderVariantCount; ++variantIndex) - { - AZ::RPI::ShaderVariantListSourceData::VariantInfo shaderVariantInfo; - ShaderManagementConsoleDocumentRequestBus::EventResult( - shaderVariantInfo, documentId, &ShaderManagementConsoleDocumentRequestBus::Events::GetShaderVariantInfo, variantIndex); - - model->setHeaderData(variantIndex, Qt::Vertical, QString::number(variantIndex)); - - for (const auto& shaderOption : shaderVariantInfo.m_options) - { - AZ::Name optionName{ shaderOption.first }; - AZ::Name optionValue{ shaderOption.second }; - - auto optionIt = optionNames.find(optionName.GetCStr()); - int optionIndex = static_cast(AZStd::distance(optionNames.begin(), optionIt)); - - QStandardItem* item = new QStandardItem(optionValue.GetCStr()); - model->setItem(variantIndex, optionIndex, item); - } - } - - // The document tab contains a table view. - auto contentWidget = new QTableView(centralWidget()); - contentWidget->setSelectionBehavior(QAbstractItemView::SelectRows); - contentWidget->setModel(model); - return contentWidget; + return new ShaderManagementConsoleTableView(m_toolId, documentId, centralWidget()); } } // namespace ShaderManagementConsole diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.h b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.h index d5dd04c434..5ebb2005b9 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.h +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.h @@ -8,16 +8,8 @@ #pragma once -#if !defined(Q_MOC_RUN) -#include -#include #include -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT -#include -AZ_POP_DISABLE_WARNING -#endif - namespace ShaderManagementConsole { //! ShaderManagementConsoleWindow is the main class. Its responsibility is limited to initializing and connecting @@ -31,7 +23,7 @@ namespace ShaderManagementConsole using Base = AtomToolsFramework::AtomToolsDocumentMainWindow; - ShaderManagementConsoleWindow(QWidget* parent = 0); + ShaderManagementConsoleWindow(const AZ::Crc32& toolId, QWidget* parent = 0); ~ShaderManagementConsoleWindow() = default; protected: diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsole_files.cmake b/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsole_files.cmake index 332cd73b40..c66405ac15 100644 --- a/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsole_files.cmake +++ b/Gems/Atom/Tools/ShaderManagementConsole/Code/shadermanagementconsole_files.cmake @@ -11,6 +11,8 @@ set(FILES Source/Document/ShaderManagementConsoleDocument.cpp Source/Document/ShaderManagementConsoleDocument.h + Source/Window/ShaderManagementConsoleTableView.h + Source/Window/ShaderManagementConsoleTableView.cpp Source/Window/ShaderManagementConsoleWindow.h Source/Window/ShaderManagementConsoleWindow.cpp Source/Window/ShaderManagementConsole.qrc @@ -18,6 +20,5 @@ set(FILES Source/main.cpp Source/ShaderManagementConsoleApplication.cpp Source/ShaderManagementConsoleApplication.h - Source/ShaderManagementConsoleRequestBus.h ../Scripts/GenerateShaderVariantListForMaterials.py ) diff --git a/Gems/Atom/Tools/ShaderManagementConsole/Scripts/GenerateShaderVariantListForMaterials.py b/Gems/Atom/Tools/ShaderManagementConsole/Scripts/GenerateShaderVariantListForMaterials.py index c31047d347..79a0391bf7 100755 --- a/Gems/Atom/Tools/ShaderManagementConsole/Scripts/GenerateShaderVariantListForMaterials.py +++ b/Gems/Atom/Tools/ShaderManagementConsole/Scripts/GenerateShaderVariantListForMaterials.py @@ -38,28 +38,16 @@ def main(): if extension != "shader": print("The input argument for the script is not a valid .shader file") return - - # Get info such as relative path of the file and asset id - shaderAssetInfo = azlmbr.shadermanagementconsole.ShaderManagementConsoleRequestBus( - azlmbr.bus.Broadcast, - 'GetSourceAssetInfo', - filename - ) - - # retrieves a list of all material source files that use the shader. Note that materials inherit from materialtype files, which are actual files that refer to shader files. - materialAssetIds = azlmbr.shadermanagementconsole.ShaderManagementConsoleRequestBus( - azlmbr.bus.Broadcast, - 'FindMaterialAssetsUsingShader', - shaderAssetInfo.relativePath - ) - + + # prompt the user to save the file in the project folder or same folder as the shader msgBox = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Question, "Choose Save Location for .shadervariantlist File", "Save .shadervariantlist file in Project folder or in the same folder as shader file?" ) + projectButton = msgBox.addButton("Project Folder", QtWidgets.QMessageBox.AcceptRole) - msgBox.addButton("Same Folder as Shader", QtWidgets.QMessageBox.AcceptRole) + shaderButton = msgBox.addButton("Same Folder as Shader", QtWidgets.QMessageBox.AcceptRole) cancelButton = msgBox.addButton("Cancel", QtWidgets.QMessageBox.RejectRole) msgBox.exec() @@ -68,98 +56,54 @@ def main(): is_save_in_project_folder = True elif msgBox.clickedButton() == cancelButton: return - - # This loop collects all uniquely-identified shader items used by the materials based on its shader variant id. - shader_file = os.path.basename(filename) - shaderVariantIds = [] - shaderVariantListShaderOptionGroups = [] - progressDialog = QtWidgets.QProgressDialog(f"Generating .shadervariantlist file for:\n{shader_file}", "Cancel", 0, len(materialAssetIds)) - progressDialog.setMaximumWidth(400) - progressDialog.setMaximumHeight(100) - progressDialog.setModal(True) - progressDialog.setWindowTitle("Generating Shader Variant List") - for i, materialAssetId in enumerate(materialAssetIds): - materialInstanceShaderItems = azlmbr.shadermanagementconsole.ShaderManagementConsoleRequestBus(azlmbr.bus.Broadcast, 'GetMaterialInstanceShaderItems', materialAssetId) - - for shaderItem in materialInstanceShaderItems: - shaderAssetId = shaderItem.GetShaderAsset().get_id() - if shaderAssetInfo.assetId == shaderAssetId: - shaderVariantId = shaderItem.GetShaderVariantId() - if not shaderVariantId.IsEmpty(): - # Check for repeat shader variant ids. We are using a list here - # instead of a set to check for duplicates on shaderVariantIds because - # shaderVariantId is not hashed by the ID like it is in the C++ side. - has_repeat = False - for variantId in shaderVariantIds: - if shaderVariantId == variantId: - has_repeat = True - break - if has_repeat: - continue - - shaderVariantIds.append(shaderVariantId) - shaderVariantListShaderOptionGroups.append(shaderItem.GetShaderOptionGroup()) - - progressDialog.setValue(i) - if progressDialog.wasCanceled(): - return - - progressDialog.close() - - # Generate the shader variant list data by collecting shader option name-value pairs.s - shaderVariantList = azlmbr.shader.ShaderVariantListSourceData() - shaderVariantList.shaderFilePath = shaderAssetInfo.relativePath - shaderVariants = [] - stableId = 1 - for shaderOptionGroup in shaderVariantListShaderOptionGroups: - variantInfo = azlmbr.shader.ShaderVariantInfo() - variantInfo.stableId = stableId - options = {} - - shaderOptionDescriptors = shaderOptionGroup.GetShaderOptionDescriptors() - for shaderOptionDescriptor in shaderOptionDescriptors: - optionName = shaderOptionDescriptor.GetName() - optionValue = shaderOptionGroup.GetValueByOptionName(optionName) - if not optionValue.IsValid(): - continue - - valueName = shaderOptionDescriptor.GetValueName(optionValue) - options[optionName.ToString()] = valueName.ToString() - - if len(options) != 0: - variantInfo.options = options - shaderVariants.append(variantInfo) - stableId += 1 - - shaderVariantList.shaderVariants = shaderVariants + + # open the shader file as a document which will generate all of the shader variant list data + documentId = azlmbr.atomtools.AtomToolsDocumentSystemRequestBus( + azlmbr.bus.Broadcast, + 'OpenDocument', + filename + ) + + if documentId.IsNull(): + print("The shader source data file could not be opened") + return + + # get the shader variant list data object which is only needed for the shader file path + shaderVariantList = azlmbr.shadermanagementconsole.ShaderManagementConsoleDocumentRequestBus( + azlmbr.bus.Event, + 'GetShaderVariantListSourceData', + documentId + ) + + # generate the default save file path by replacing the extension of the open shader file + pre, ext = os.path.splitext(filename) + defaultShaderVariantListFilePath = f'{pre}.shadervariantlist' # clean previously generated shader variant list file so they don't clash. - pre, ext = os.path.splitext(shaderAssetInfo.relativePath) + pre, ext = os.path.splitext(shaderVariantList.shaderFilePath) projectShaderVariantListFilePath = os.path.join(azlmbr.paths.projectroot, PROJECT_SHADER_VARIANTS_FOLDER, f'{pre}.shadervariantlist') - pre, ext = os.path.splitext(filename) - defaultShaderVariantListFilePath = f'{pre}.shadervariantlist' - clean_existing_shadervariantlist_files([ projectShaderVariantListFilePath ]) + # Save the shader variant list file if is_save_in_project_folder: shaderVariantListFilePath = projectShaderVariantListFilePath else: shaderVariantListFilePath = defaultShaderVariantListFilePath - + shaderVariantListFilePath = shaderVariantListFilePath.replace("\\", "/") - azlmbr.shader.SaveShaderVariantListSourceData(shaderVariantListFilePath, shaderVariantList) - - # Open the document in shader management console - result = azlmbr.atomtools.AtomToolsDocumentSystemRequestBus( + + # Save the document in shader management console + saveResult = azlmbr.atomtools.AtomToolsDocumentSystemRequestBus( azlmbr.bus.Broadcast, - 'OpenDocument', + 'SaveDocumentAsChild', + documentId, shaderVariantListFilePath ) - - if not result.IsNull(): + + if saveResult: msgBox = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Information, "Shader Variant List File Successfully Generated", @@ -167,7 +111,7 @@ def main(): QtWidgets.QMessageBox.Ok ) msgBox.exec() - + print("==== End shader variant script ============================================================") if __name__ == "__main__": diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/FlyCameraInputComponent.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/FlyCameraInputComponent.cpp index 79f8d4708a..d418c8ebcf 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/FlyCameraInputComponent.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/FlyCameraInputComponent.cpp @@ -102,6 +102,7 @@ void FlyCameraInputComponent::Reflect(AZ::ReflectContext* reflection) ->Attribute("Category", "Gameplay") ->Attribute("Icon", "Icons/Components/FlyCameraInput.svg") ->Attribute("ViewportIcon", "Icons/Components/Viewport/FlyCameraInput.svg") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/gameplay/fly-camera-input/") ->Attribute("AutoExpand", true) ->Attribute("AppearsInAddComponentMenu", AZ_CRC("Game", 0x232b318c)) ->DataElement(0, &FlyCameraInputComponent::m_moveSpeed, "Move Speed", "Speed at which the camera moves") diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.cpp index 0250640d65..65efd83109 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/EditorCommonFeaturesSystemComponent.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -217,8 +216,8 @@ namespace AZ using namespace AzToolsFramework::Thumbnailer; using namespace LyIntegration; - ThumbnailerRequestsBus::Broadcast( - &ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(SharedThumbnailCache), ThumbnailContext::DefaultContext); + ThumbnailerRequestBus::Broadcast( + &ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(SharedThumbnailCache)); if (!m_thumbnailRenderer) { @@ -236,9 +235,7 @@ namespace AZ using namespace AzToolsFramework::Thumbnailer; using namespace LyIntegration; - ThumbnailerRequestsBus::Broadcast( - &ThumbnailerRequests::UnregisterThumbnailProvider, SharedThumbnailCache::ProviderName, - ThumbnailContext::DefaultContext); + ThumbnailerRequestBus::Broadcast(&ThumbnailerRequests::UnregisterThumbnailProvider, SharedThumbnailCache::ProviderName); m_thumbnailRenderer.reset(); m_previewerFactory.reset(); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp index 67db519669..a907f106e2 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/ColorGrading/EditorHDRColorGradingComponent.cpp @@ -40,7 +40,6 @@ namespace AZ ->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. ->ClassElement(AZ::Edit::ClassElements::Group, "LUT Generation") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->UIElement(AZ::Edit::UIHandlers::Button, "Generate LUT", "Generates a LUT from the scene's enabled color grading blend.") diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/EditorRadiusWeightModifierComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/EditorRadiusWeightModifierComponent.cpp index d4d86d6496..e0d0fe204d 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/EditorRadiusWeightModifierComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/RadiusWeightModifier/EditorRadiusWeightModifierComponent.cpp @@ -32,7 +32,7 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg") ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/radius-weight-modifier/") + ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/postfx-radius-weight-modifier/") ; editContext->Class("RadiusWeightModifierComponentController", "") diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewer.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewer.cpp index 942af55721..0f96b2964d 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewer.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedPreviewer.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -51,7 +50,7 @@ namespace AZ using namespace AzToolsFramework::Thumbnailer; auto thumbnailKey = entry->GetThumbnailKey(); - m_ui->m_previewWidget->SetThumbnailKey(thumbnailKey, ThumbnailContext::DefaultContext); + m_ui->m_previewWidget->SetThumbnailKey(thumbnailKey); m_fileInfo = QString::fromUtf8(entry->GetName().c_str()); UpdateFileInfo(); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnail.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnail.cpp index d07c4577e8..82221afcc5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnail.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SharedPreview/SharedThumbnail.cpp @@ -35,8 +35,15 @@ namespace AZ m_state = State::Failed; } + SharedThumbnail::~SharedThumbnail() + { + AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusDisconnect(); + AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); + } + void SharedThumbnail::LoadThread() { + m_state = State::Loading; AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::QueueEvent( m_assetInfo.m_assetType, &AzToolsFramework::Thumbnailer::ThumbnailerRendererRequests::RenderThumbnail, m_key, SharedThumbnailSize); @@ -45,15 +52,10 @@ namespace AZ m_renderWait.acquire(); } - SharedThumbnail::~SharedThumbnail() - { - AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusDisconnect(); - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); - } - void SharedThumbnail::ThumbnailRendered(const QPixmap& thumbnailImage) { m_pixmap = thumbnailImage; + m_state = State::Ready; m_renderWait.release(); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp index 2686ca8a16..6d9dad33b3 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp @@ -144,44 +144,46 @@ namespace SurfaceData return false; } - bool SurfaceDataMeshComponent::DoRayTrace(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const + void SurfaceDataMeshComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const { - AZStd::shared_lock lock(m_cacheMutex); + GetSurfacePointsFromList(AZStd::span(&inPosition, 1), surfacePointList); + } - // test AABB as first pass to claim the point - const AZ::Vector3 testPosition = AZ::Vector3( - inPosition.GetX(), - inPosition.GetY(), - (m_meshBounds.GetMax().GetZ() + m_meshBounds.GetMin().GetZ()) * 0.5f); + void SurfaceDataMeshComponent::GetSurfacePointsFromList( + AZStd::span inPositions, SurfacePointList& surfacePointList) const + { + AZ::Vector3 hitPosition; + AZ::Vector3 hitNormal; - if (!m_meshBounds.Contains(testPosition)) - { - return false; - } + AZStd::shared_lock lock(m_cacheMutex); AZ::RPI::ModelAsset* mesh = m_meshAssetData.GetAs(); if (!mesh) { - return false; + return; } - const AZ::Vector3 rayStart = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_meshBounds.GetMax().GetZ() + s_rayAABBHeightPadding); - const AZ::Vector3 rayEnd = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_meshBounds.GetMin().GetZ() - s_rayAABBHeightPadding); - return GetMeshRayIntersection( - *mesh, m_meshWorldTM, m_meshWorldTMInverse, m_meshNonUniformScale, rayStart, rayEnd, outPosition, outNormal); - } - - - void SurfaceDataMeshComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const - { - AZ::Vector3 hitPosition; - AZ::Vector3 hitNormal; - if (DoRayTrace(inPosition, hitPosition, hitNormal)) + for (auto& inPosition : inPositions) { - surfacePointList.AddSurfacePoint(GetEntityId(), hitPosition, hitNormal, m_newPointWeights); + // test AABB as first pass to claim the point + if (SurfaceData::AabbContains2D(m_meshBounds, inPosition)) + { + const AZ::Vector3 rayStart = + AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_meshBounds.GetMax().GetZ() + s_rayAABBHeightPadding); + const AZ::Vector3 rayEnd = + AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_meshBounds.GetMin().GetZ() - s_rayAABBHeightPadding); + bool rayHit = GetMeshRayIntersection( + *mesh, m_meshWorldTM, m_meshWorldTMInverse, m_meshNonUniformScale, rayStart, rayEnd, hitPosition, hitNormal); + + if (rayHit) + { + surfacePointList.AddSurfacePoint(GetEntityId(), inPosition, hitPosition, hitNormal, m_newPointWeights); + } + } } } + AZ::Aabb SurfaceDataMeshComponent::GetSurfaceAabb() const { return m_meshBounds; @@ -255,6 +257,7 @@ namespace SurfaceData registryEntry.m_entityId = GetEntityId(); registryEntry.m_bounds = GetSurfaceAabb(); registryEntry.m_tags = GetSurfaceTags(); + registryEntry.m_maxPointsCreatedPerInput = 1; if (!meshValidBeforeUpdate && !meshValidAfterUpdate) { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.h index 4a8d3cf427..8b993eb55e 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.h @@ -80,9 +80,9 @@ namespace SurfaceData //////////////////////////////////////////////////////////////////////// // SurfaceDataProviderRequestBus void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const override; + void GetSurfacePointsFromList(AZStd::span inPositions, SurfacePointList& surfacePointList) const override; private: - bool DoRayTrace(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const; void UpdateMeshData(); void OnCompositionChanged(); diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp index deb668d12a..efc1137fa7 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -33,7 +34,7 @@ namespace AZ::Render m_auxGeomFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity(entityId); } - void AtomActorDebugDraw::DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags, EMotionFX::ActorInstance* instance) + void AtomActorDebugDraw::DebugDraw(const EMotionFX::ActorRenderFlags& renderFlags, EMotionFX::ActorInstance* instance) { if (!m_auxGeomFeatureProcessor || !instance) { @@ -46,10 +47,12 @@ namespace AZ::Render return; } + using AZ::RHI::CheckBitsAny; + // Update the mesh deformers (perform cpu skinning and morphing) when needed. - if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_AABB] || renderFlags[EMotionFX::ActorRenderFlag::RENDER_FACENORMALS] || - renderFlags[EMotionFX::ActorRenderFlag::RENDER_TANGENTS] || renderFlags[EMotionFX::ActorRenderFlag::RENDER_VERTEXNORMALS] || - renderFlags[EMotionFX::ActorRenderFlag::RENDER_WIREFRAME]) + if (CheckBitsAny(renderFlags, + EMotionFX::ActorRenderFlags::AABB | EMotionFX::ActorRenderFlags::FaceNormals | EMotionFX::ActorRenderFlags::Tangents | + EMotionFX::ActorRenderFlags::VertexNormals | EMotionFX::ActorRenderFlags::Wireframe)) { instance->UpdateMeshDeformers(0.0f, true); } @@ -61,7 +64,7 @@ namespace AZ::Render const float scaleMultiplier = CalculateScaleMultiplier(instance); // Render aabb - if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_AABB]) + if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::AABB)) { RenderAABB(instance, renderActorSettings.m_enabledNodeBasedAabb, renderActorSettings.m_nodeAABBColor, @@ -70,39 +73,39 @@ namespace AZ::Render } // Render simple line skeleton - if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_LINESKELETON]) + if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::LineSkeleton)) { - RenderLineSkeleton(instance, renderActorSettings.m_lineSkeletonColor); + RenderLineSkeleton(debugDisplay, instance, renderActorSettings.m_lineSkeletonColor); } // Render advanced skeleton - if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_SKELETON]) + if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::Skeleton)) { - RenderSkeleton(instance, renderActorSettings.m_skeletonColor); + RenderSkeleton(debugDisplay, instance, renderActorSettings.m_skeletonColor); } - if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_NODENAMES]) + if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::NodeNames)) { RenderJointNames(instance, viewport, renderActorSettings.m_jointNameColor); } // Render internal EMFX debug lines. - if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_EMFX_DEBUG]) + if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::EmfxDebug)) { RenderEMFXDebugDraw(instance); } // Render - if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_NODEORIENTATION]) + if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::NodeOrientation)) { RenderNodeOrientations(instance, debugDisplay, renderActorSettings.m_nodeOrientationScale * scaleMultiplier); } // Render vertex normal, face normal, tagent and wireframe. - const bool renderVertexNormals = renderFlags[EMotionFX::ActorRenderFlag::RENDER_VERTEXNORMALS]; - const bool renderFaceNormals = renderFlags[EMotionFX::ActorRenderFlag::RENDER_FACENORMALS]; - const bool renderTangents = renderFlags[EMotionFX::ActorRenderFlag::RENDER_TANGENTS]; - const bool renderWireframe = renderFlags[EMotionFX::ActorRenderFlag::RENDER_WIREFRAME]; + const bool renderVertexNormals = CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::VertexNormals); + const bool renderFaceNormals = CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::FaceNormals); + const bool renderTangents = CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::Tangents); + const bool renderWireframe = CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::Wireframe); if (renderVertexNormals || renderFaceNormals || renderTangents || renderWireframe) { @@ -136,6 +139,35 @@ namespace AZ::Render } } } + + // Hit detection colliders + if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::HitDetectionColliders)) + { + RenderColliders(debugDisplay, EMotionFX::PhysicsSetup::HitDetection, instance, + renderActorSettings.m_hitDetectionColliderColor, renderActorSettings.m_selectedHitDetectionColliderColor); + } + + // Cloth colliders + if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::ClothColliders)) + { + RenderColliders(debugDisplay, EMotionFX::PhysicsSetup::Cloth, instance, + renderActorSettings.m_clothColliderColor, renderActorSettings.m_selectedClothColliderColor); + } + + // Simulated object colliders + if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::SimulatedObjectColliders)) + { + RenderColliders(debugDisplay, EMotionFX::PhysicsSetup::SimulatedObjectCollider, instance, + renderActorSettings.m_simulatedObjectColliderColor, renderActorSettings.m_selectedSimulatedObjectColliderColor); + } + + // Ragdoll + const bool renderRagdollColliders = AZ::RHI::CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::RagdollColliders); + const bool renderRagdollJointLimits = AZ::RHI::CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::RagdollJointLimits); + if (renderRagdollColliders || renderRagdollJointLimits) + { + RenderRagdoll(debugDisplay, instance, renderRagdollColliders, renderRagdollJointLimits); + } } float AtomActorDebugDraw::CalculateScaleMultiplier(EMotionFX::ActorInstance* instance) const @@ -246,27 +278,22 @@ namespace AZ::Render } } - void AtomActorDebugDraw::RenderLineSkeleton(EMotionFX::ActorInstance* instance, const AZ::Color& skeletonColor) + void AtomActorDebugDraw::RenderLineSkeleton(AzFramework::DebugDisplayRequests* debugDisplay, EMotionFX::ActorInstance* instance, const AZ::Color& color) const { - RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); - const EMotionFX::TransformData* transformData = instance->GetTransformData(); const EMotionFX::Skeleton* skeleton = instance->GetActor()->GetSkeleton(); const EMotionFX::Pose* pose = transformData->GetCurrentPose(); - const size_t lodLevel = instance->GetLODLevel(); const size_t numJoints = skeleton->GetNumNodes(); - m_auxVertices.clear(); - m_auxVertices.reserve(numJoints * 2); - m_auxColors.clear(); - m_auxColors.reserve(numJoints * 2); - AZ::Color renderColor; - const AZStd::unordered_set* cachedSelectedJointIndices; EMotionFX::JointSelectionRequestBus::BroadcastResult( cachedSelectedJointIndices, &EMotionFX::JointSelectionRequests::FindSelectedJointIndices, instance); + const AZ::u32 oldState = debugDisplay->GetState(); + debugDisplay->DepthTestOff(); + + AZ::Color renderColor; for (size_t jointIndex = 0; jointIndex < numJoints; ++jointIndex) { const EMotionFX::Node* joint = skeleton->GetNode(jointIndex); @@ -287,41 +314,34 @@ namespace AZ::Render } else { - renderColor = skeletonColor; + renderColor = color; } const AZ::Vector3 parentPos = pose->GetWorldSpaceTransform(parentIndex).m_position; - m_auxVertices.emplace_back(parentPos); - m_auxColors.emplace_back(renderColor); - const AZ::Vector3 bonePos = pose->GetWorldSpaceTransform(jointIndex).m_position; - m_auxVertices.emplace_back(bonePos); - m_auxColors.emplace_back(renderColor); + + debugDisplay->SetColor(renderColor); + debugDisplay->DrawLine(parentPos, bonePos); } - RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs; - lineArgs.m_verts = m_auxVertices.data(); - lineArgs.m_vertCount = aznumeric_cast(m_auxVertices.size()); - lineArgs.m_colors = m_auxColors.data(); - lineArgs.m_colorCount = lineArgs.m_vertCount; - lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; - auxGeom->DrawLines(lineArgs); + debugDisplay->SetState(oldState); } - void AtomActorDebugDraw::RenderSkeleton(EMotionFX::ActorInstance* instance, const AZ::Color& skeletonColor) + void AtomActorDebugDraw::RenderSkeleton(AzFramework::DebugDisplayRequests* debugDisplay, EMotionFX::ActorInstance* instance, const AZ::Color& color) { - RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); - const EMotionFX::TransformData* transformData = instance->GetTransformData(); const EMotionFX::Skeleton* skeleton = instance->GetActor()->GetSkeleton(); const EMotionFX::Pose* pose = transformData->GetCurrentPose(); const size_t numEnabled = instance->GetNumEnabledNodes(); - AZ::Color renderColor = skeletonColor; const AZStd::unordered_set* cachedSelectedJointIndices; EMotionFX::JointSelectionRequestBus::BroadcastResult( cachedSelectedJointIndices, &EMotionFX::JointSelectionRequests::FindSelectedJointIndices, instance); + const AZ::u32 oldState = debugDisplay->GetState(); + debugDisplay->DepthTestOff(); + + AZ::Color renderColor; for (size_t i = 0; i < numEnabled; ++i) { EMotionFX::Node* joint = skeleton->GetNode(instance->GetEnabledNode(i)); @@ -350,12 +370,17 @@ namespace AZ::Render } else { - renderColor = skeletonColor; + renderColor = color; } + renderColor.SetA(0.75f); + debugDisplay->SetColor(renderColor); + // Render the bone cylinder, the cylinder will be directed towards the node's parent and must fit between the spheres - auxGeom->DrawCylinder(centerWorldPos, boneDirection, boneScale, cylinderSize, renderColor); - auxGeom->DrawSphere(nodeWorldPos, boneScale, renderColor); + debugDisplay->DrawSolidCylinder(centerWorldPos, boneDirection, boneScale * 0.75f, cylinderSize); + debugDisplay->DrawBall(nodeWorldPos, boneScale); } + + debugDisplay->SetState(oldState); } void AtomActorDebugDraw::RenderEMFXDebugDraw(EMotionFX::ActorInstance* instance) @@ -470,7 +495,7 @@ namespace AZ::Render lineArgs.m_vertCount = aznumeric_cast(m_auxVertices.size()); lineArgs.m_colors = &faceNormalsColor; lineArgs.m_colorCount = 1; - lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; + lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::On; auxGeom->DrawLines(lineArgs); } } @@ -504,7 +529,7 @@ namespace AZ::Render lineArgs.m_vertCount = aznumeric_cast(m_auxVertices.size()); lineArgs.m_colors = &vertexNormalsColor; lineArgs.m_colorCount = 1; - lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; + lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::On; auxGeom->DrawLines(lineArgs); } } @@ -592,7 +617,7 @@ namespace AZ::Render lineArgs.m_vertCount = aznumeric_cast(m_auxVertices.size()); lineArgs.m_colors = m_auxColors.data(); lineArgs.m_colorCount = aznumeric_cast(m_auxColors.size()); - lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; + lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::On; auxGeom->DrawLines(lineArgs); } @@ -612,7 +637,6 @@ namespace AZ::Render } PrepareForMesh(mesh, worldTM); - const AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_NORMALS); const size_t numSubMeshes = mesh->GetNumSubMeshes(); @@ -652,7 +676,7 @@ namespace AZ::Render lineArgs.m_vertCount = aznumeric_cast(m_auxVertices.size()); lineArgs.m_colors = &wireframeColor; lineArgs.m_colorCount = 1; - lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off; + lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::On; auxGeom->DrawLines(lineArgs); } } @@ -811,4 +835,270 @@ namespace AZ::Render } } } + + void AtomActorDebugDraw::RenderColliders(AzFramework::DebugDisplayRequests* debugDisplay, + const AzPhysics::ShapeColliderPairList& colliders, + const EMotionFX::ActorInstance* actorInstance, + const EMotionFX::Node* node, + const AZ::Color& colliderColor) const + { + if (!debugDisplay) + { + return; + } + + const size_t nodeIndex = node->GetNodeIndex(); + + for (const auto& collider : colliders) + { +#ifndef EMFX_SCALE_DISABLED + const AZ::Vector3& worldScale = actorInstance->GetTransformData()->GetCurrentPose()->GetModelSpaceTransform(nodeIndex).m_scale; +#else + const AZ::Vector3 worldScale = AZ::Vector3::CreateOne(); +#endif + + const EMotionFX::Transform colliderOffsetTransform(collider.first->m_position, collider.first->m_rotation); + const EMotionFX::Transform& actorInstanceGlobalTransform = actorInstance->GetWorldSpaceTransform(); + const EMotionFX::Transform& emfxNodeGlobalTransform = + actorInstance->GetTransformData()->GetCurrentPose()->GetModelSpaceTransform(nodeIndex); + const EMotionFX::Transform emfxColliderGlobalTransformNoScale = + colliderOffsetTransform * emfxNodeGlobalTransform * actorInstanceGlobalTransform; + + const AZ::TypeId colliderType = collider.second->RTTI_GetType(); + if (colliderType == azrtti_typeid()) + { + Physics::SphereShapeConfiguration* sphere = static_cast(collider.second.get()); + + // O3DE Physics scaling rules: The maximum component from the node scale will be multiplied by the radius of the sphere. + const float radius = sphere->m_radius * + MCore::Max3(static_cast(worldScale.GetX()), static_cast(worldScale.GetY()), + static_cast(worldScale.GetZ())); + + debugDisplay->DepthTestOff(); + debugDisplay->SetColor(colliderColor); + debugDisplay->DrawWireSphere(emfxColliderGlobalTransformNoScale.m_position, radius); + } + else if (colliderType == azrtti_typeid()) + { + Physics::CapsuleShapeConfiguration* capsule = static_cast(collider.second.get()); + + // O3DE Physics scaling rules: The maximum of the X/Y scale components of the node scale will be multiplied by the radius of + // the capsule. The Z component of the entity scale will be multiplied by the height of the capsule. + const float radius = + capsule->m_radius * MCore::Max(static_cast(worldScale.GetX()), static_cast(worldScale.GetY())); + const float height = capsule->m_height * static_cast(worldScale.GetZ()); + + debugDisplay->DepthTestOff(); + debugDisplay->SetColor(colliderColor); + debugDisplay->DrawWireCapsule( + emfxColliderGlobalTransformNoScale.m_position, emfxColliderGlobalTransformNoScale.ToAZTransform().GetBasisZ(), radius, height); + } + else if (colliderType == azrtti_typeid()) + { + Physics::BoxShapeConfiguration* box = static_cast(collider.second.get()); + + // O3DE Physics scaling rules: Each component of the box dimensions will be scaled by the node's world scale. + AZ::Vector3 dimensions = box->m_dimensions; + dimensions *= worldScale; + + debugDisplay->DepthTestOff(); + debugDisplay->SetColor(colliderColor); + debugDisplay->DrawWireBox( + emfxColliderGlobalTransformNoScale.m_position, emfxColliderGlobalTransformNoScale.m_position + dimensions); + } + } + } + + void AtomActorDebugDraw::RenderColliders(AzFramework::DebugDisplayRequests* debugDisplay, + EMotionFX::PhysicsSetup::ColliderConfigType colliderConfigType, + EMotionFX::ActorInstance* actorInstance, + const AZ::Color& defaultColor, + const AZ::Color& selectedColor) const + { + if (colliderConfigType == EMotionFX::PhysicsSetup::Unknown) + { + return; + } + + const EMotionFX::Actor* actor = actorInstance->GetActor(); + const AZStd::shared_ptr& physicsSetup = actor->GetPhysicsSetup(); + const Physics::CharacterColliderConfiguration* colliderConfig = physicsSetup->GetColliderConfigByType(colliderConfigType); + + if (colliderConfig) + { + const AZStd::unordered_set* cachedSelectedJointIndices; + EMotionFX::JointSelectionRequestBus::BroadcastResult( + cachedSelectedJointIndices, &EMotionFX::JointSelectionRequests::FindSelectedJointIndices, actorInstance); + + for (const Physics::CharacterColliderNodeConfiguration& nodeConfig : colliderConfig->m_nodes) + { + const EMotionFX::Node* joint = actor->GetSkeleton()->FindNodeByName(nodeConfig.m_name.c_str()); + if (joint) + { + const bool jointSelected = cachedSelectedJointIndices && + (cachedSelectedJointIndices->empty() || cachedSelectedJointIndices->find(joint->GetNodeIndex()) != cachedSelectedJointIndices->end()); + + const AzPhysics::ShapeColliderPairList& colliders = nodeConfig.m_shapes; + RenderColliders(debugDisplay, colliders, actorInstance, joint, jointSelected ? selectedColor : defaultColor); + } + } + } + } + + void AtomActorDebugDraw::RenderJointFrame(AzFramework::DebugDisplayRequests* debugDisplay, + const AzPhysics::JointConfiguration& configuration, + const EMotionFX::ActorInstance* actorInstance, + const EMotionFX::Node* node, + const AZ::Color& color) const + { + const EMotionFX::Transform& actorInstanceWorldSpaceTransform = actorInstance->GetWorldSpaceTransform(); + const EMotionFX::Pose* currentPose = actorInstance->GetTransformData()->GetCurrentPose(); + const EMotionFX::Transform childJointLocalSpaceTransform(AZ::Vector3::CreateZero(), configuration.m_childLocalRotation); + const EMotionFX::Transform childModelSpaceTransform = + childJointLocalSpaceTransform * currentPose->GetModelSpaceTransform(node->GetNodeIndex()); + const EMotionFX::Transform jointChildWorldSpaceTransformNoScale = (childModelSpaceTransform * actorInstanceWorldSpaceTransform); + AZ::Vector3 dir = jointChildWorldSpaceTransformNoScale.ToAZTransform().GetBasisX(); + + debugDisplay->SetColor(color); + debugDisplay->DrawArrow(jointChildWorldSpaceTransformNoScale.m_position, jointChildWorldSpaceTransformNoScale.m_position + dir, 0.1f); + } + + void AtomActorDebugDraw::JointLimitRenderData::Clear() + { + m_vertexBuffer.clear(); + m_indexBuffer.clear(); + m_lineBuffer.clear(); + m_lineValidityBuffer.clear(); + } + + void AtomActorDebugDraw::RenderJointLimit(AzFramework::DebugDisplayRequests* debugDisplay, + const AzPhysics::JointConfiguration& configuration, + const EMotionFX::ActorInstance* actorInstance, + const EMotionFX::Node* node, + const EMotionFX::Node* parentNode, + const AZ::Color& regularColor, + const AZ::Color& violatedColor) + { + const size_t nodeIndex = node->GetNodeIndex(); + const size_t parentNodeIndex = parentNode->GetNodeIndex(); + const EMotionFX::Transform& actorInstanceWorldTransform = actorInstance->GetWorldSpaceTransform(); + const EMotionFX::Pose* currentPose = actorInstance->GetTransformData()->GetCurrentPose(); + const AZ::Quaternion& parentOrientation = currentPose->GetModelSpaceTransform(parentNodeIndex).m_rotation; + const AZ::Quaternion& childOrientation = currentPose->GetModelSpaceTransform(nodeIndex).m_rotation; + + m_jointLimitRenderData.Clear(); + if (auto* jointHelpers = AZ::Interface::Get()) + { + jointHelpers->GenerateJointLimitVisualizationData( + configuration, parentOrientation, childOrientation, s_scale, s_angularSubdivisions, s_radialSubdivisions, + m_jointLimitRenderData.m_vertexBuffer, m_jointLimitRenderData.m_indexBuffer, + m_jointLimitRenderData.m_lineBuffer, m_jointLimitRenderData.m_lineValidityBuffer); + } + + EMotionFX::Transform jointModelSpaceTransform = currentPose->GetModelSpaceTransform(parentNodeIndex); + jointModelSpaceTransform.m_position = currentPose->GetModelSpaceTransform(nodeIndex).m_position; + const EMotionFX::Transform jointGlobalTransformNoScale = jointModelSpaceTransform * actorInstanceWorldTransform; + + const size_t numLineBufferEntries = m_jointLimitRenderData.m_lineBuffer.size(); + if (m_jointLimitRenderData.m_lineValidityBuffer.size() * 2 != numLineBufferEntries) + { + AZ_ErrorOnce("EMotionFX", false, "Unexpected buffer size in joint limit visualization for node %s", node->GetName()); + return; + } + + for (size_t i = 0; i < numLineBufferEntries; i += 2) + { + const AZ::Color& lineColor = m_jointLimitRenderData.m_lineValidityBuffer[i / 2] ? regularColor : violatedColor; + debugDisplay->DepthTestOff(); + debugDisplay->DrawLine( + jointGlobalTransformNoScale.TransformPoint(m_jointLimitRenderData.m_lineBuffer[i]), + jointGlobalTransformNoScale.TransformPoint(m_jointLimitRenderData.m_lineBuffer[i + 1]), lineColor.GetAsVector4(), lineColor.GetAsVector4() + ); + } + } + + void AtomActorDebugDraw::RenderRagdoll(AzFramework::DebugDisplayRequests* debugDisplay, + EMotionFX::ActorInstance* actorInstance, + bool renderColliders, + bool renderJointLimits) + { + const EMotionFX::Actor* actor = actorInstance->GetActor(); + const EMotionFX::Skeleton* skeleton = actor->GetSkeleton(); + const size_t numNodes = skeleton->GetNumNodes(); + const AZStd::shared_ptr& physicsSetup = actor->GetPhysicsSetup(); + const Physics::RagdollConfiguration& ragdollConfig = physicsSetup->GetRagdollConfig(); + const AZStd::vector& ragdollNodes = ragdollConfig.m_nodes; + const Physics::CharacterColliderConfiguration& colliderConfig = ragdollConfig.m_colliders; + const EMotionFX::RagdollInstance* ragdollInstance = actorInstance->GetRagdollInstance(); + + const AZ::Render::RenderActorSettings& settings = EMotionFX::GetRenderActorSettings(); + const AZ::Color& violatedColor = settings.m_violatedJointLimitColor; + const AZ::Color& defaultColor = settings.m_ragdollColliderColor; + const AZ::Color& selectedColor = settings.m_selectedRagdollColliderColor; + + const AZStd::unordered_set* cachedSelectedJointIndices; + EMotionFX::JointSelectionRequestBus::BroadcastResult( + cachedSelectedJointIndices, &EMotionFX::JointSelectionRequests::FindSelectedJointIndices, actorInstance); + + for (size_t nodeIndex = 0; nodeIndex < numNodes; ++nodeIndex) + { + const EMotionFX::Node* joint = skeleton->GetNode(nodeIndex); + const size_t jointIndex = joint->GetNodeIndex(); + + AZ::Outcome ragdollNodeIndex = AZ::Failure(); + if (ragdollInstance) + { + ragdollNodeIndex = ragdollInstance->GetRagdollNodeIndex(jointIndex); + } + else + { + ragdollNodeIndex = ragdollConfig.FindNodeConfigIndexByName(joint->GetNameString()); + } + + if (!ragdollNodeIndex.IsSuccess()) + { + continue; + } + + const bool jointSelected = cachedSelectedJointIndices && + (cachedSelectedJointIndices->empty() || cachedSelectedJointIndices->find(joint->GetNodeIndex()) != cachedSelectedJointIndices->end()); + + AZ::Color finalColor; + if (jointSelected) + { + finalColor = selectedColor; + } + else + { + finalColor = defaultColor; + } + + const Physics::RagdollNodeConfiguration& ragdollNode = ragdollNodes[ragdollNodeIndex.GetValue()]; + + if (renderColliders) + { + const Physics::CharacterColliderNodeConfiguration* colliderNodeConfig = + colliderConfig.FindNodeConfigByName(joint->GetNameString()); + if (colliderNodeConfig) + { + const AzPhysics::ShapeColliderPairList& colliders = colliderNodeConfig->m_shapes; + RenderColliders(debugDisplay, colliders, actorInstance, joint, finalColor); + } + } + + if (renderJointLimits && jointSelected) + { + const AZStd::shared_ptr& jointLimitConfig = ragdollNode.m_jointConfig; + if (jointLimitConfig) + { + const EMotionFX::Node* ragdollParentNode = physicsSetup->FindRagdollParentNode(joint); + if (ragdollParentNode) + { + RenderJointLimit(debugDisplay, *jointLimitConfig, actorInstance, joint, ragdollParentNode, finalColor, violatedColor); + RenderJointFrame(debugDisplay, *jointLimitConfig, actorInstance, joint, finalColor); + } + } + } + } + } } // namespace AZ::Render diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h index 8e985fdbc6..011eae637b 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h @@ -40,10 +40,9 @@ namespace AZ::Render public: AtomActorDebugDraw(AZ::EntityId entityId); - void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags, EMotionFX::ActorInstance* instance); + void DebugDraw(const EMotionFX::ActorRenderFlags& renderFlags, EMotionFX::ActorInstance* instance); private: - float CalculateBoneScale(EMotionFX::ActorInstance* actorInstance, EMotionFX::Node* node); float CalculateScaleMultiplier(EMotionFX::ActorInstance* instance) const; void PrepareForMesh(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM); @@ -56,8 +55,12 @@ namespace AZ::Render const AZ::Color& meshAabbColor, bool enableStaticAabb, const AZ::Color& staticAabbColor); - void RenderLineSkeleton(EMotionFX::ActorInstance* instance, const AZ::Color& skeletonColor); - void RenderSkeleton(EMotionFX::ActorInstance* instance, const AZ::Color& skeletonColor); + void RenderLineSkeleton(AzFramework::DebugDisplayRequests* debugDisplay, + EMotionFX::ActorInstance* instance, + const AZ::Color& skeletonColor) const; + void RenderSkeleton(AzFramework::DebugDisplayRequests* debugDisplay, + EMotionFX::ActorInstance* instance, + const AZ::Color& color); void RenderEMFXDebugDraw(EMotionFX::ActorInstance* instance); void RenderNormals( EMotionFX::Mesh* mesh, @@ -83,6 +86,37 @@ namespace AZ::Render bool selected, //!< Set to true if you want to render the axis using the selection color. */ bool renderAxisName = false); + void RenderColliders(AzFramework::DebugDisplayRequests* debugDisplay, + const AzPhysics::ShapeColliderPairList& colliders, + const EMotionFX::ActorInstance* actorInstance, + const EMotionFX::Node* node, + const AZ::Color& colliderColor) const; + + void RenderColliders(AzFramework::DebugDisplayRequests* debugDisplay, + EMotionFX::PhysicsSetup::ColliderConfigType colliderConfigType, + EMotionFX::ActorInstance* actorInstance, + const AZ::Color& defaultColor, + const AZ::Color& selectedColor) const; + + void RenderJointFrame(AzFramework::DebugDisplayRequests* debugDisplay, + const AzPhysics::JointConfiguration& configuration, + const EMotionFX::ActorInstance* actorInstance, + const EMotionFX::Node* node, + const AZ::Color& color) const; + + // Ragdoll + void RenderJointLimit(AzFramework::DebugDisplayRequests* debugDisplay, + const AzPhysics::JointConfiguration& configuration, + const EMotionFX::ActorInstance* actorInstance, + const EMotionFX::Node* node, + const EMotionFX::Node* parentNode, + const AZ::Color& regularColor, + const AZ::Color& violatedColor); + void RenderRagdoll(AzFramework::DebugDisplayRequests* debugDisplay, + EMotionFX::ActorInstance* actorInstance, + bool renderColliders, + bool renderJointLimits); + EMotionFX::Mesh* m_currentMesh = nullptr; //!< A pointer to the mesh whose world space positions are in the pre-calculated positions buffer. //!< NULL in case we haven't pre-calculated any positions yet. AZStd::vector m_worldSpacePositions; //!< The buffer used to store world space positions for rendering normals @@ -97,6 +131,22 @@ namespace AZ::Render AZStd::vector m_auxColors; EntityId m_entityId; + // Joint limits + static constexpr float s_scale = 0.1f; + static constexpr AZ::u32 s_angularSubdivisions = 32; + static constexpr AZ::u32 s_radialSubdivisions = 2; + + struct JointLimitRenderData + { + AZStd::vector m_vertexBuffer; + AZStd::vector m_indexBuffer; + AZStd::vector m_lineBuffer; + AZStd::vector m_lineValidityBuffer; + + void Clear(); + }; + JointLimitRenderData m_jointLimitRenderData; + AzFramework::TextDrawParameters m_drawParams; AzFramework::FontDrawInterface* m_fontDrawInterface = nullptr; }; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp index 35aea57702..f0b46255fe 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp @@ -79,7 +79,7 @@ namespace AZ::Render UpdateBounds(); } - void AtomActorInstance::DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags) + void AtomActorInstance::DebugDraw(const EMotionFX::ActorRenderFlags& renderFlags) { m_atomActorDebugDraw->DebugDraw(renderFlags, m_actorInstance); } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h index 73d428216c..2eeb61c812 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h @@ -86,7 +86,7 @@ namespace AZ // RenderActorInstance overrides ... void OnTick(float timeDelta) override; - void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags); + void DebugDraw(const EMotionFX::ActorRenderFlags& renderFlags); void UpdateBounds() override; void SetMaterials(const EMotionFX::Integration::ActorAsset::MaterialList& materialPerLOD) override { AZ_UNUSED(materialPerLOD); }; void SetSkinningMethod(EMotionFX::Integration::SkinningMethod emfxSkinningMethod) override; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp index 6ac2a66261..63a0554e37 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp @@ -191,7 +191,7 @@ namespace EMStudio return result; } - void AnimViewportRenderer::UpdateActorRenderFlag(EMotionFX::ActorRenderFlagBitset renderFlags) + void AnimViewportRenderer::UpdateActorRenderFlag(EMotionFX::ActorRenderFlags renderFlags) { for (AZ::Entity* entity : m_actorEntities) { diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h index 0066c222db..3f34d2f26e 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h @@ -54,7 +54,7 @@ namespace EMStudio //! Return the center position of the existing objects. AZ::Vector3 GetCharacterCenter() const; - void UpdateActorRenderFlag(EMotionFX::ActorRenderFlagBitset renderFlags); + void UpdateActorRenderFlag(EMotionFX::ActorRenderFlags renderFlags); AZStd::shared_ptr GetFrameworkScene() const; AZ::EntityId GetEntityId() const; AzFramework::EntityContextId GetEntityContextId() const; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRequestBus.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRequestBus.h index 9b2fc0b212..91de0882bc 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRequestBus.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRequestBus.h @@ -26,7 +26,7 @@ namespace EMStudio virtual void UpdateCameraFollowUp(bool followUp) = 0; //! Update render flags - virtual void UpdateRenderFlags(EMotionFX::ActorRenderFlagBitset renderFlags) = 0; + virtual void UpdateRenderFlags(EMotionFX::ActorRenderFlags renderFlags) = 0; }; using AnimViewportRequestBus = AZ::EBus; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportSettings.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportSettings.cpp index 347e077af3..78dbcd6230 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportSettings.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportSettings.cpp @@ -22,8 +22,10 @@ namespace EMStudio::ViewportUtil constexpr AZStd::string_view CameraTranslateUpIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateUpId"; constexpr AZStd::string_view CameraTranslateDownIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateUpDownId"; constexpr AZStd::string_view CameraTranslateBoostIdSetting = "/Amazon/Preferences/Editor/Camera/TranslateBoostId"; + constexpr AZStd::string_view CameraOrbitIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitId"; + constexpr AZStd::string_view CameraDefaultOrbitDistanceSetting = "/Amazon/Preferences/Editor/Camera/DefaultOrbitDistance"; - AzFramework::TranslateCameraInputChannelIds BuildTranslateCameraInputChannelIds() + AzFramework::TranslateCameraInputChannelIds TranslateCameraInputChannelIds() { AzFramework::TranslateCameraInputChannelIds translateCameraInputChannelIds; translateCameraInputChannelIds.m_leftChannelId = @@ -64,7 +66,23 @@ namespace EMStudio::ViewportUtil return GetRegistry(CameraTranslateSmoothingSetting, true); } - AzFramework::InputChannelId BuildRotateCameraInputId() + float CameraDefaultOrbitDistance() + { + return aznumeric_cast(GetRegistry(CameraDefaultOrbitDistanceSetting, 5.0)); + } + + AzFramework::InputChannelId RotateCameraInputChannelId() + { + return AzFramework::InputChannelId(GetRegistry(CameraOrbitLookIdSetting, AZStd::string("mouse_button_right")).c_str()); + } + + AzFramework::InputChannelId OrbitCameraInputChannelId() + { + return AzFramework::InputChannelId( + GetRegistry(CameraOrbitIdSetting, AZStd::string("keyboard_key_modifier_alt_l")).c_str()); + } + + AzFramework::InputChannelId OrbitLookCameraInputChannelId() { return AzFramework::InputChannelId(GetRegistry(CameraOrbitLookIdSetting, AZStd::string("mouse_button_left")).c_str()); } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportSettings.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportSettings.h index 5fa14a8aae..4fca274866 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportSettings.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportSettings.h @@ -31,7 +31,10 @@ namespace EMStudio::ViewportUtil float CameraTranslateSmoothness(); bool CameraRotateSmoothingEnabled(); bool CameraTranslateSmoothingEnabled(); + float CameraDefaultOrbitDistance(); - AzFramework::TranslateCameraInputChannelIds BuildTranslateCameraInputChannelIds(); - AzFramework::InputChannelId BuildRotateCameraInputId(); + AzFramework::TranslateCameraInputChannelIds TranslateCameraInputChannelIds(); + AzFramework::InputChannelId RotateCameraInputChannelId(); + AzFramework::InputChannelId OrbitCameraInputChannelId(); + AzFramework::InputChannelId OrbitLookCameraInputChannelId(); } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp index d9de41f54b..e2c011fcde 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp @@ -61,35 +61,35 @@ namespace EMStudio renderOptionsButton->setIcon(QIcon(":/EMotionFXAtom/Visualization.svg")); addWidget(renderOptionsButton); - CreateViewOptionEntry(contextMenu, "Solid", EMotionFX::ActorRenderFlag::RENDER_SOLID); - CreateViewOptionEntry(contextMenu, "Wireframe", EMotionFX::ActorRenderFlag::RENDER_WIREFRAME); + CreateViewOptionEntry(contextMenu, "Solid", EMotionFX::ActorRenderFlagIndex::SOLID); + CreateViewOptionEntry(contextMenu, "Wireframe", EMotionFX::ActorRenderFlagIndex::WIREFRAME); // [EMFX-TODO] Add those option once implemented. - // CreateViewOptionEntry(contextMenu, "Lighting", EMotionFX::ActorRenderFlag::RENDER_LIGHTING); - // CreateViewOptionEntry(contextMenu, "Backface Culling", EMotionFX::ActorRenderFlag::RENDER_BACKFACECULLING); + // CreateViewOptionEntry(contextMenu, "Lighting", EMotionFX::ActorRenderFlagIndex::RENDER_LIGHTING); + // CreateViewOptionEntry(contextMenu, "Backface Culling", EMotionFX::ActorRenderFlagIndex::RENDER_BACKFACECULLING); contextMenu->addSeparator(); - CreateViewOptionEntry(contextMenu, "Vertex Normals", EMotionFX::ActorRenderFlag::RENDER_VERTEXNORMALS); - CreateViewOptionEntry(contextMenu, "Face Normals", EMotionFX::ActorRenderFlag::RENDER_FACENORMALS); - CreateViewOptionEntry(contextMenu, "Tangents", EMotionFX::ActorRenderFlag::RENDER_TANGENTS); - CreateViewOptionEntry(contextMenu, "Actor Bounding Boxes", EMotionFX::ActorRenderFlag::RENDER_AABB); + CreateViewOptionEntry(contextMenu, "Vertex Normals", EMotionFX::ActorRenderFlagIndex::VERTEXNORMALS); + CreateViewOptionEntry(contextMenu, "Face Normals", EMotionFX::ActorRenderFlagIndex::FACENORMALS); + CreateViewOptionEntry(contextMenu, "Tangents", EMotionFX::ActorRenderFlagIndex::TANGENTS); + CreateViewOptionEntry(contextMenu, "Actor Bounding Boxes", EMotionFX::ActorRenderFlagIndex::AABB); contextMenu->addSeparator(); - CreateViewOptionEntry(contextMenu, "Line Skeleton", EMotionFX::ActorRenderFlag::RENDER_LINESKELETON); - CreateViewOptionEntry(contextMenu, "Solid Skeleton", EMotionFX::ActorRenderFlag::RENDER_SKELETON); - CreateViewOptionEntry(contextMenu, "Joint Names", EMotionFX::ActorRenderFlag::RENDER_NODENAMES); - CreateViewOptionEntry(contextMenu, "Joint Orientations", EMotionFX::ActorRenderFlag::RENDER_NODEORIENTATION); + CreateViewOptionEntry(contextMenu, "Line Skeleton", EMotionFX::ActorRenderFlagIndex::LINESKELETON); + CreateViewOptionEntry(contextMenu, "Solid Skeleton", EMotionFX::ActorRenderFlagIndex::SKELETON); + CreateViewOptionEntry(contextMenu, "Joint Names", EMotionFX::ActorRenderFlagIndex::NODENAMES); + CreateViewOptionEntry(contextMenu, "Joint Orientations", EMotionFX::ActorRenderFlagIndex::NODEORIENTATION); // [EMFX-TODO] Add those option once implemented. - // CreateViewOptionEntry(contextMenu, "Actor Bind Pose", EMotionFX::ActorRenderFlag::RENDER_ACTORBINDPOSE); + // CreateViewOptionEntry(contextMenu, "Actor Bind Pose", EMotionFX::ActorRenderFlagIndex::RENDER_ACTORBINDPOSE); contextMenu->addSeparator(); - CreateViewOptionEntry(contextMenu, "Hit Detection Colliders", EMotionFX::ActorRenderFlag::RENDER_HITDETECTION_COLLIDERS, true, + CreateViewOptionEntry(contextMenu, "Hit Detection Colliders", EMotionFX::ActorRenderFlagIndex::HITDETECTION_COLLIDERS, true, ":/EMotionFXAtom/HitDetection.svg"); - CreateViewOptionEntry(contextMenu, "Ragdoll Colliders", EMotionFX::ActorRenderFlag::RENDER_RAGDOLL_COLLIDERS, true, + CreateViewOptionEntry(contextMenu, "Ragdoll Colliders", EMotionFX::ActorRenderFlagIndex::RAGDOLL_COLLIDERS, true, ":/EMotionFXAtom/RagdollCollider.svg"); - CreateViewOptionEntry(contextMenu, "Ragdoll Joint Limits", EMotionFX::ActorRenderFlag::RENDER_RAGDOLL_JOINTLIMITS, true, + CreateViewOptionEntry(contextMenu, "Ragdoll Joint Limits", EMotionFX::ActorRenderFlagIndex::RAGDOLL_JOINTLIMITS, true, ":/EMotionFXAtom/RagdollJointLimit.svg"); - CreateViewOptionEntry(contextMenu, "Cloth Colliders", EMotionFX::ActorRenderFlag::RENDER_CLOTH_COLLIDERS, true, + CreateViewOptionEntry(contextMenu, "Cloth Colliders", EMotionFX::ActorRenderFlagIndex::CLOTH_COLLIDERS, true, ":/EMotionFXAtom/Cloth.svg"); - CreateViewOptionEntry(contextMenu, "Simulated Object Colliders", EMotionFX::ActorRenderFlag::RENDER_SIMULATEDOBJECT_COLLIDERS, true, + CreateViewOptionEntry(contextMenu, "Simulated Object Colliders", EMotionFX::ActorRenderFlagIndex::SIMULATEDOBJECT_COLLIDERS, true, ":/EMotionFXAtom/SimulatedObjectCollider.svg"); - CreateViewOptionEntry(contextMenu, "Simulated Joints", EMotionFX::ActorRenderFlag::RENDER_SIMULATEJOINTS); + CreateViewOptionEntry(contextMenu, "Simulated Joints", EMotionFX::ActorRenderFlagIndex::SIMULATEJOINTS); } // Add the camera button @@ -158,7 +158,7 @@ namespace EMStudio } void AnimViewportToolBar::CreateViewOptionEntry( - QMenu* menu, const char* menuEntryName, uint32_t actionIndex, bool visible, const char* iconFileName) + QMenu* menu, const char* menuEntryName, AZ::u8 actionIndex, bool visible, const char* iconFileName) { QAction* action = menu->addAction( menuEntryName, @@ -194,13 +194,13 @@ namespace EMStudio m_manipulatorActions[mode]->setChecked(true); } - const EMotionFX::ActorRenderFlagBitset renderFlags = renderOptions->GetRenderFlags(); - for (size_t i = 0; i < renderFlags.size(); ++i) + const EMotionFX::ActorRenderFlags renderFlags = renderOptions->GetRenderFlags(); + for (uint8 i = 0; i < EMotionFX::ActorRenderFlagIndex::NUM_RENDERFLAGINDEXES; ++i) { QAction* action = m_renderActions[i]; if (action) { - action->setChecked(renderFlags[i]); + action->setChecked(EMotionFX::ActorRenderFlagUtil::CheckBit(renderFlags, i)); } } } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.h index f516b9df28..08c32429e9 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.h @@ -30,11 +30,11 @@ namespace EMStudio private: void CreateViewOptionEntry( - QMenu* menu, const char* menuEntryName, uint32_t actionIndex, bool visible = true, const char* iconFileName = nullptr); + QMenu* menu, const char* menuEntryName, AZ::u8 actionIndex, bool visible = true, const char* iconFileName = nullptr); AtomRenderPlugin* m_plugin = nullptr; QAction* m_manipulatorActions[RenderOptions::ManipulatorMode::NUM_MODES] = { nullptr }; - QAction* m_renderActions[EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS] = { nullptr }; + QAction* m_renderActions[EMotionFX::ActorRenderFlagIndex::NUM_RENDERFLAGINDEXES] = { nullptr }; QAction* m_followCharacterAction = nullptr; }; } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp index 90fbb81e47..45c0061771 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp @@ -64,17 +64,31 @@ namespace EMStudio void AnimViewportWidget::SetupCameras() { - m_rotateCamera = AZStd::make_shared(EMStudio::ViewportUtil::BuildRotateCameraInputId()); + m_rotateCamera = AZStd::make_shared(EMStudio::ViewportUtil::RotateCameraInputChannelId()); - const auto translateCameraInputChannelIds = EMStudio::ViewportUtil::BuildTranslateCameraInputChannelIds(); + const auto translateCameraInputChannelIds = EMStudio::ViewportUtil::TranslateCameraInputChannelIds(); m_translateCamera = AZStd::make_shared( translateCameraInputChannelIds, AzFramework::LookTranslation, AzFramework::TranslatePivotLook); m_translateCamera.get()->m_translateSpeedFn = [] { return 3.0f; }; + m_lookScrollCamera = AZStd::make_shared(); + m_orbitCamera = AZStd::make_shared(EMStudio::ViewportUtil::OrbitCameraInputChannelId()); + m_orbitCamera->SetPivotFn( + [this](const AZ::Vector3& position, const AZ::Vector3& direction) + { + if (m_orbitCamera->Beginning()) + { + m_defaultOrbitPoint = position + direction * EMStudio::ViewportUtil::CameraDefaultOrbitDistance(); + } + return m_defaultOrbitPoint; + }); + m_orbitRotateCamera = AZStd::make_shared(EMStudio::ViewportUtil::OrbitLookCameraInputChannelId()); m_orbitDollyScrollCamera = AZStd::make_shared(); + m_orbitCamera->m_orbitCameras.AddCamera(m_orbitRotateCamera); + m_orbitCamera->m_orbitCameras.AddCamera(m_orbitDollyScrollCamera); } void AnimViewportWidget::SetupCameraController() @@ -122,7 +136,8 @@ namespace EMStudio { cameras.AddCamera(m_rotateCamera); cameras.AddCamera(m_translateCamera); - cameras.AddCamera(m_orbitDollyScrollCamera); + cameras.AddCamera(m_lookScrollCamera); + cameras.AddCamera(m_orbitCamera); }); GetControllerList()->Add(controller); } @@ -229,7 +244,7 @@ namespace EMStudio } } - void AnimViewportWidget::UpdateRenderFlags(EMotionFX::ActorRenderFlagBitset renderFlags) + void AnimViewportWidget::UpdateRenderFlags(EMotionFX::ActorRenderFlags renderFlags) { m_renderer->UpdateActorRenderFlag(renderFlags); } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h index 9e4a7c5fa7..b66e002c68 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h @@ -45,7 +45,7 @@ namespace EMStudio // AnimViewportRequestBus::Handler overrides void UpdateCameraViewMode(RenderOptions::CameraViewMode mode); void UpdateCameraFollowUp(bool follow); - void UpdateRenderFlags(EMotionFX::ActorRenderFlagBitset renderFlags); + void UpdateRenderFlags(EMotionFX::ActorRenderFlags renderFlags); // ViewportPluginRequestBus::Handler overrides AZ::s32 GetViewportId() const; @@ -54,8 +54,13 @@ namespace EMStudio AtomRenderPlugin* m_plugin; AZStd::unique_ptr m_renderer; + AZStd::shared_ptr m_rotateCamera; AZStd::shared_ptr m_translateCamera; + AZStd::shared_ptr m_lookScrollCamera; AZStd::shared_ptr m_orbitDollyScrollCamera; + AZStd::shared_ptr m_orbitCamera; + AZStd::shared_ptr m_orbitRotateCamera; + AZ::Vector3 m_defaultOrbitPoint = AZ::Vector3::CreateZero(); }; } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp index 5b3f305cd0..32298d740f 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.cpp @@ -300,7 +300,7 @@ namespace EMStudio return &m_renderOptions; } - void AtomRenderPlugin::Render([[maybe_unused]]EMotionFX::ActorRenderFlagBitset renderFlags) + void AtomRenderPlugin::Render([[maybe_unused]]EMotionFX::ActorRenderFlags renderFlags) { if (!m_animViewportWidget) { diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.h index 2207690628..66e89dfc45 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AtomRenderPlugin.h @@ -61,7 +61,7 @@ namespace EMStudio void SaveRenderOptions(); RenderOptions* GetRenderOptions(); - void Render(EMotionFX::ActorRenderFlagBitset renderFlags) override; + void Render(EMotionFX::ActorRenderFlags renderFlags) override; void SetManipulatorMode(RenderOptions::ManipulatorMode mode); private: diff --git a/Gems/AtomTressFX/Code/Components/EditorHairComponent.cpp b/Gems/AtomTressFX/Code/Components/EditorHairComponent.cpp index 9271400d2b..6edcbbe6cf 100644 --- a/Gems/AtomTressFX/Code/Components/EditorHairComponent.cpp +++ b/Gems/AtomTressFX/Code/Components/EditorHairComponent.cpp @@ -37,7 +37,6 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "editor/icons/components/viewport/component_placeholder.png") ->Attribute(Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(Edit::Attributes::AutoExpand, true) - ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/gems/reference/rendering/amd/atom-tressfx/") ; editContext->Class( diff --git a/Gems/Blast/Code/Source/Components/BlastFamilyComponent.h b/Gems/Blast/Code/Source/Components/BlastFamilyComponent.h index 99957d0fce..1a0e8135d9 100644 --- a/Gems/Blast/Code/Source/Components/BlastFamilyComponent.h +++ b/Gems/Blast/Code/Source/Components/BlastFamilyComponent.h @@ -7,7 +7,7 @@ */ #pragma once -#include +#include #include #include #include diff --git a/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp b/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp index 90f530895a..5224c3dfe5 100644 --- a/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp +++ b/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp @@ -37,9 +37,6 @@ namespace Blast ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Box.png") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Box.png") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute( - AZ::Edit::Attributes::HelpPageURL, - "https://o3de.org/docs/user-guide/components/reference/destruction/blast-family/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement( AZ::Edit::UIHandlers::Default, &EditorBlastFamilyComponent::m_blastAsset, "Blast asset", diff --git a/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp b/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp index 83085b5f9c..b5733f72eb 100644 --- a/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp +++ b/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp @@ -60,9 +60,6 @@ namespace Blast ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Box.png") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Box.png") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute( - AZ::Edit::Attributes::HelpPageURL, - "https://o3de.org/docs/user-guide/components/reference/destruction/blast-family-mesh-data/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement( AZ::Edit::UIHandlers::CheckBox, &EditorBlastMeshDataComponent::m_showMeshAssets, diff --git a/Gems/Blast/Code/Source/Family/DamageManager.h b/Gems/Blast/Code/Source/Family/DamageManager.h index ed8d03da18..28892074b0 100644 --- a/Gems/Blast/Code/Source/Family/DamageManager.h +++ b/Gems/Blast/Code/Source/Family/DamageManager.h @@ -83,7 +83,7 @@ namespace Blast static AZ::Vector3 TransformToLocal(BlastActor& actor, const AZ::Vector3& globalPosition); BlastMaterial m_blastMaterial; - ActorTracker& m_actorTracker; + [[maybe_unused]] ActorTracker& m_actorTracker; }; template diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioPlugin.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioPlugin.h index 2dc86ba01b..1d6bd55290 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioPlugin.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioPlugin.h @@ -96,7 +96,7 @@ namespace EMStudio virtual void LegacyRender(RenderPlugin* renderPlugin, RenderInfo* renderInfo) { MCORE_UNUSED(renderPlugin); MCORE_UNUSED(renderInfo); } //! Render function will call atom auxGeom internally to render. This is the replacement for LegacyRender function. - virtual void Render(EMotionFX::ActorRenderFlagBitset renderFlags) + virtual void Render(EMotionFX::ActorRenderFlags renderFlags) { AZ_UNUSED(renderFlags); }; diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MotionEventPresetManager.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MotionEventPresetManager.cpp index ac5dd80f4f..b807b2b80d 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MotionEventPresetManager.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MotionEventPresetManager.cpp @@ -302,25 +302,28 @@ namespace EMStudio } } - // Check if motion event with this configuration exists and return color. AZ::u32 MotionEventPresetManager::GetEventColor(const EMotionFX::EventDataSet& eventDatas) const { for (const MotionEventPreset* preset : m_eventPresets) { - EMotionFX::EventDataSet commonDatas; const EMotionFX::EventDataSet& presetDatas = preset->GetEventDatas(); - const bool allMatch = AZStd::all_of(presetDatas.cbegin(), presetDatas.cend(), [eventDatas](const EMotionFX::EventDataPtr& presetData) + + const size_t numEventDatas = eventDatas.size(); + if (numEventDatas == presetDatas.size()) { - const auto thisPresetDataHasMatch = AZStd::find_if(eventDatas.cbegin(), eventDatas.cend(), [presetData](const EMotionFX::EventDataPtr& eventData) + for (size_t i = 0; i < numEventDatas; ++i) { - return ((presetData && eventData && *presetData == *eventData) || (!presetData && !eventData)); - }); - return thisPresetDataHasMatch != eventDatas.cend(); - }); - if (allMatch) - { - return preset->GetEventColor(); + const EMotionFX::EventDataPtr& eventData = eventDatas[i]; + const EMotionFX::EventDataPtr& presetData = presetDatas[i]; + + if (eventData && presetData && + eventData->RTTI_GetType() == presetData->RTTI_GetType() && + *eventData == *presetData) + { + return preset->GetEventColor(); + } + } } } diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp index 7adea15a99..4c3b9d1a93 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp @@ -255,12 +255,7 @@ namespace EMStudio settings->setValue("cameraFollowUp", m_cameraFollowUp); // Save render flags - settings->beginGroup("renderFlags"); - for (uint32 i = 0; i < EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS; ++i) - { - settings->setValue(QString(i), (bool)m_renderFlags[i]); - } - settings->endGroup(); + settings->setValue("renderFlags", static_cast(m_renderFlags)); } RenderOptions RenderOptions::Load(QSettings* settings) @@ -333,14 +328,8 @@ namespace EMStudio options.m_cameraFollowUp = settings->value("CameraFollowUp", options.m_cameraFollowUp).toBool(); // Read render flags - settings->beginGroup("renderFlags"); - for (uint32 i = 0; i < EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS; ++i) - { - const bool defaultValue = (i == EMotionFX::ActorRenderFlag::RENDER_SOLID); - const bool isEnabled = settings->value(QString(i), defaultValue).toBool(); - options.m_renderFlags[i] = isEnabled; - } - settings->endGroup(); + options.m_renderFlags = + EMotionFX::ActorRenderFlags(settings->value("RenderFlags", static_cast(EMotionFX::ActorRenderFlags::Default)).toInt()); options.CopyToRenderActorSettings(EMotionFX::GetRenderActorSettings()); @@ -1104,17 +1093,17 @@ namespace EMStudio return m_cameraFollowUp; } - void RenderOptions::ToggerRenderFlag(int index) + void RenderOptions::ToggerRenderFlag(uint8 index) { - m_renderFlags[index] = !m_renderFlags[index]; + m_renderFlags ^= EMotionFX::ActorRenderFlags(AZ_BIT(index)); } - void RenderOptions::SetRenderFlags(EMotionFX::ActorRenderFlagBitset renderFlags) + void RenderOptions::SetRenderFlags(EMotionFX::ActorRenderFlags renderFlags) { m_renderFlags = renderFlags; } - EMotionFX::ActorRenderFlagBitset RenderOptions::GetRenderFlags() const + EMotionFX::ActorRenderFlags RenderOptions::GetRenderFlags() const { return m_renderFlags; } diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.h index 3c5dbcaba1..09b23cab08 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.h @@ -284,9 +284,9 @@ namespace EMStudio void SetCameraFollowUp(bool followUp); bool GetCameraFollowUp() const; - void ToggerRenderFlag(int index); - void SetRenderFlags(EMotionFX::ActorRenderFlagBitset renderFlags); - EMotionFX::ActorRenderFlagBitset GetRenderFlags() const; + void ToggerRenderFlag(uint8 index); + void SetRenderFlags(EMotionFX::ActorRenderFlags renderFlags); + EMotionFX::ActorRenderFlags GetRenderFlags() const; private: void OnGridUnitSizeChangedCallback() const; @@ -406,7 +406,7 @@ namespace EMStudio ManipulatorMode m_manipulatorMode = ManipulatorMode::SELECT; CameraViewMode m_cameraViewMode = CameraViewMode::DEFAULT; bool m_cameraFollowUp = false; - EMotionFX::ActorRenderFlagBitset m_renderFlags; + EMotionFX::ActorRenderFlags m_renderFlags; }; } // namespace EMStudio diff --git a/Gems/EMotionFX/Code/MCore/Source/StringConversions.cpp b/Gems/EMotionFX/Code/MCore/Source/StringConversions.cpp index 88a22db245..5f57a036c2 100644 --- a/Gems/EMotionFX/Code/MCore/Source/StringConversions.cpp +++ b/Gems/EMotionFX/Code/MCore/Source/StringConversions.cpp @@ -76,11 +76,6 @@ namespace MCore namespace AZStd { - void to_string(string& str, bool value) - { - str = value ? "true" : "false"; - } - void to_string(string& str, const AZ::Vector2& value) { str = AZStd::string::format("%.8f,%.8f", diff --git a/Gems/EMotionFX/Code/MCore/Source/StringConversions.h b/Gems/EMotionFX/Code/MCore/Source/StringConversions.h index fd1c59208d..01b41091cf 100644 --- a/Gems/EMotionFX/Code/MCore/Source/StringConversions.h +++ b/Gems/EMotionFX/Code/MCore/Source/StringConversions.h @@ -49,7 +49,6 @@ namespace MCore namespace AZStd { - void to_string(string& str, bool value); void to_string(string& str, const AZ::Vector2& value); void to_string(string& str, const AZ::Vector3& value); void to_string(string& str, const AZ::Vector4& value); @@ -57,7 +56,6 @@ namespace AZStd void to_string(string& str, const AZ::Matrix4x4& value); void to_string(string& str, const AZ::Transform& value); - inline AZStd::string to_string(bool val) { AZStd::string str; to_string(str, val); return str; } inline AZStd::string to_string(const AZ::Vector2& val) { AZStd::string str; to_string(str, val); return str; } inline AZStd::string to_string(const AZ::Vector3& val) { AZStd::string str; to_string(str, val); return str; } inline AZStd::string to_string(const AZ::Vector4& val) { AZStd::string str; to_string(str, val); return str; } diff --git a/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.cpp index 13696298dc..57ae1f8415 100644 --- a/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.cpp @@ -713,121 +713,6 @@ namespace EMotionFX renderUtil->EnableLighting(oldLightingEnabled); } - void ColliderContainerWidget::RenderColliders( - const AzPhysics::ShapeColliderPairList& colliders, - const ActorInstance* actorInstance, - const Node* node, - const AZ::Color& colliderColor) - { - const size_t nodeIndex = node->GetNodeIndex(); - - for (const auto& collider : colliders) - { -#ifndef EMFX_SCALE_DISABLED - const AZ::Vector3& worldScale = actorInstance->GetTransformData()->GetCurrentPose()->GetModelSpaceTransform(nodeIndex).m_scale; -#else - const AZ::Vector3 worldScale = AZ::Vector3::CreateOne(); -#endif - - const Transform colliderOffsetTransform(collider.first->m_position, collider.first->m_rotation); - const Transform& actorInstanceGlobalTransform = actorInstance->GetWorldSpaceTransform(); - const Transform& emfxNodeGlobalTransform = - actorInstance->GetTransformData()->GetCurrentPose()->GetModelSpaceTransform(nodeIndex); - const Transform emfxColliderGlobalTransformNoScale = - colliderOffsetTransform * emfxNodeGlobalTransform * actorInstanceGlobalTransform; - - AZ::s32 viewportId = -1; - EMStudio::ViewportPluginRequestBus::BroadcastResult(viewportId, &EMStudio::ViewportPluginRequestBus::Events::GetViewportId); - AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; - AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, viewportId); - AzFramework::DebugDisplayRequests* debugDisplay = nullptr; - debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); - if (!debugDisplay) - { - return; - } - - const AZ::TypeId colliderType = collider.second->RTTI_GetType(); - if (colliderType == azrtti_typeid()) - { - Physics::SphereShapeConfiguration* sphere = static_cast(collider.second.get()); - - // O3DE Physics scaling rules: The maximum component from the node scale will be multiplied by the radius of the sphere. - const float radius = sphere->m_radius * - MCore::Max3(static_cast(worldScale.GetX()), static_cast(worldScale.GetY()), - static_cast(worldScale.GetZ())); - - debugDisplay->DepthTestOff(); - debugDisplay->SetColor(colliderColor); - debugDisplay->DrawWireSphere(emfxColliderGlobalTransformNoScale.m_position, radius); - } - else if (colliderType == azrtti_typeid()) - { - Physics::CapsuleShapeConfiguration* capsule = static_cast(collider.second.get()); - - // O3DE Physics scaling rules: The maximum of the X/Y scale components of the node scale will be multiplied by the radius of - // the capsule. The Z component of the entity scale will be multiplied by the height of the capsule. - const float radius = - capsule->m_radius * MCore::Max(static_cast(worldScale.GetX()), static_cast(worldScale.GetY())); - const float height = capsule->m_height * static_cast(worldScale.GetZ()); - - debugDisplay->DepthTestOff(); - debugDisplay->SetColor(colliderColor); - debugDisplay->DrawWireCapsule( - emfxColliderGlobalTransformNoScale.m_position, emfxColliderGlobalTransformNoScale.ToAZTransform().GetBasisZ(), radius, height); - } - else if (colliderType == azrtti_typeid()) - { - Physics::BoxShapeConfiguration* box = static_cast(collider.second.get()); - - // O3DE Physics scaling rules: Each component of the box dimensions will be scaled by the node's world scale. - AZ::Vector3 dimensions = box->m_dimensions; - dimensions *= worldScale; - - debugDisplay->DepthTestOff(); - debugDisplay->SetColor(colliderColor); - debugDisplay->DrawWireBox( - emfxColliderGlobalTransformNoScale.m_position, emfxColliderGlobalTransformNoScale.m_position + dimensions); - } - } - } - - void ColliderContainerWidget::RenderColliders(PhysicsSetup::ColliderConfigType colliderConfigType, - const AZ::Color& defaultColor, const AZ::Color& selectedColor) - { - if (colliderConfigType == PhysicsSetup::Unknown) - { - return; - } - - const AZStd::unordered_set& selectedJointIndices = EMStudio::GetManager()->GetSelectedJointIndices(); - - const ActorManager* actorManager = GetEMotionFX().GetActorManager(); - const size_t actorInstanceCount = actorManager->GetNumActorInstances(); - for (size_t i = 0; i < actorInstanceCount; ++i) - { - const ActorInstance* actorInstance = actorManager->GetActorInstance(i); - const Actor* actor = actorInstance->GetActor(); - const AZStd::shared_ptr& physicsSetup = actor->GetPhysicsSetup(); - const Physics::CharacterColliderConfiguration* colliderConfig = physicsSetup->GetColliderConfigByType(colliderConfigType); - - if (colliderConfig) - { - for (const Physics::CharacterColliderNodeConfiguration& nodeConfig : colliderConfig->m_nodes) - { - const Node* joint = actor->GetSkeleton()->FindNodeByName(nodeConfig.m_name.c_str()); - if (joint) - { - const bool jointSelected = - selectedJointIndices.empty() || selectedJointIndices.find(joint->GetNodeIndex()) != selectedJointIndices.end(); - const AzPhysics::ShapeColliderPairList& colliders = nodeConfig.m_shapes; - RenderColliders(colliders, actorInstance, joint, jointSelected ? selectedColor : defaultColor); - } - } - } - } - } - /////////////////////////////////////////////////////////////////////////// ColliderContainerWidget::ColliderEditedCallback::ColliderEditedCallback(ColliderContainerWidget* parent, bool executePreUndo, bool executePreCommand) diff --git a/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.h b/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.h index 82e6d151d5..b2ddf1dc4d 100644 --- a/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.h +++ b/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.h @@ -167,17 +167,6 @@ namespace EMotionFX EMStudio::RenderPlugin* renderPlugin, EMStudio::EMStudioPlugin::RenderInfo* renderInfo); - static void RenderColliders( - const AzPhysics::ShapeColliderPairList& colliders, - const ActorInstance* actorInstance, - const Node* node, - const AZ::Color& colliderColor); - - static void RenderColliders( - PhysicsSetup::ColliderConfigType colliderConfigType, - const AZ::Color& defaultColor, - const AZ::Color& selectedColor); - static int s_layoutSpacing; signals: diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.cpp index 161cc2ac95..7bfd1ab6b3 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.cpp @@ -195,16 +195,4 @@ namespace EMotionFX renderPlugin, renderInfo); } - - void ClothJointInspectorPlugin::Render(EMotionFX::ActorRenderFlagBitset renderFlags) - { - const bool renderColliders = renderFlags[RENDER_CLOTH_COLLIDERS]; - if (!renderColliders) - { - return; - } - - const AZ::Render::RenderActorSettings& settings = EMotionFX::GetRenderActorSettings(); - ColliderContainerWidget::RenderColliders(PhysicsSetup::Cloth, settings.m_clothColliderColor, settings.m_selectedClothColliderColor); - } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.h b/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.h index c1b3e9ad13..a36ef0f2b2 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.h +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.h @@ -48,7 +48,6 @@ namespace EMotionFX void OnContextMenu(QMenu* menu, const QModelIndexList& selectedRowIndices) override; void LegacyRender(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) override; - void Render(EMotionFX::ActorRenderFlagBitset renderFlags) override; static bool IsJointInCloth(const QModelIndex& index); public slots: diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.cpp index 0f0eb89bfc..8a64849cc9 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.cpp @@ -180,19 +180,4 @@ namespace EMotionFX renderPlugin, renderInfo); } - - void HitDetectionJointInspectorPlugin::Render(EMotionFX::ActorRenderFlagBitset renderFlags) - { - const bool renderColliders = renderFlags[EMotionFX::ActorRenderFlag::RENDER_HITDETECTION_COLLIDERS]; - if (!renderColliders) - { - return; - } - - const AZ::Render::RenderActorSettings& settings = EMotionFX::GetRenderActorSettings(); - - ColliderContainerWidget::RenderColliders( - PhysicsSetup::HitDetection, settings.m_hitDetectionColliderColor, - settings.m_selectedHitDetectionColliderColor); - } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.h b/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.h index 4dc61f9b9e..e4cfc9ea6f 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.h +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.h @@ -44,7 +44,6 @@ namespace EMotionFX void OnContextMenu(QMenu* menu, const QModelIndexList& selectedRowIndices) override; void LegacyRender(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) override; - void Render(EMotionFX::ActorRenderFlagBitset renderFlags) override; public slots: void OnAddCollider(); diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.cpp index e76cf42c44..0c8642e91b 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.cpp @@ -32,10 +32,6 @@ namespace EMotionFX { - float RagdollNodeInspectorPlugin::s_scale = 0.1f; - AZ::u32 RagdollNodeInspectorPlugin::s_angularSubdivisions = 32; - AZ::u32 RagdollNodeInspectorPlugin::s_radialSubdivisions = 2; - RagdollNodeInspectorPlugin::RagdollNodeInspectorPlugin() : EMStudio::DockWidgetPlugin() , m_nodeWidget(nullptr) @@ -520,7 +516,6 @@ namespace EMotionFX const Node* ragdollParentNode = physicsSetup->FindRagdollParentNode(joint); if (ragdollParentNode) { - LegacyRenderJointLimit(*jointLimitConfig, actorInstance, joint, ragdollParentNode, renderPlugin, renderInfo, finalColor); LegacyRenderJointFrame(*jointLimitConfig, actorInstance, joint, ragdollParentNode, renderInfo, finalColor); } } @@ -528,54 +523,6 @@ namespace EMotionFX } } - void RagdollNodeInspectorPlugin::LegacyRenderJointLimit( - const AzPhysics::JointConfiguration& configuration, - const ActorInstance* actorInstance, - const Node* node, - const Node* parentNode, - EMStudio::RenderPlugin* renderPlugin, - EMStudio::EMStudioPlugin::RenderInfo* renderInfo, - const MCore::RGBAColor& color) - { - const EMStudio::RenderOptions* renderOptions = renderPlugin->GetRenderOptions(); - const MCore::RGBAColor violatedColor = renderOptions->GetViolatedJointLimitColor(); - const size_t nodeIndex = node->GetNodeIndex(); - const size_t parentNodeIndex = parentNode->GetNodeIndex(); - const Transform& actorInstanceWorldTransform = actorInstance->GetWorldSpaceTransform(); - const Pose* currentPose = actorInstance->GetTransformData()->GetCurrentPose(); - const AZ::Quaternion& parentOrientation = currentPose->GetModelSpaceTransform(parentNodeIndex).m_rotation; - const AZ::Quaternion& childOrientation = currentPose->GetModelSpaceTransform(nodeIndex).m_rotation; - - m_vertexBuffer.clear(); - m_indexBuffer.clear(); - m_lineBuffer.clear(); - m_lineValidityBuffer.clear(); - if(auto* jointHelpers = AZ::Interface::Get()) - { - jointHelpers->GenerateJointLimitVisualizationData( - configuration, parentOrientation, childOrientation, s_scale, s_angularSubdivisions, s_radialSubdivisions, m_vertexBuffer, - m_indexBuffer, m_lineBuffer, m_lineValidityBuffer); - } - - Transform jointModelSpaceTransform = currentPose->GetModelSpaceTransform(parentNodeIndex); - jointModelSpaceTransform.m_position = currentPose->GetModelSpaceTransform(nodeIndex).m_position; - const Transform jointGlobalTransformNoScale = jointModelSpaceTransform * actorInstanceWorldTransform; - - MCommon::RenderUtil* renderUtil = renderInfo->m_renderUtil; - const size_t numLineBufferEntries = m_lineBuffer.size(); - if (m_lineValidityBuffer.size() * 2 != numLineBufferEntries) - { - AZ_ErrorOnce("EMotionFX", false, "Unexpected buffer size in joint limit visualization for node %s", node->GetName()); - return; - } - - for (size_t i = 0; i < numLineBufferEntries; i += 2) - { - renderUtil->RenderLine(jointGlobalTransformNoScale.TransformPoint(m_lineBuffer[i]), - jointGlobalTransformNoScale.TransformPoint(m_lineBuffer[i + 1]), m_lineValidityBuffer[i / 2] ? color : violatedColor); - } - } - void RagdollNodeInspectorPlugin::LegacyRenderJointFrame( const AzPhysics::JointConfiguration& configuration, const ActorInstance* actorInstance, @@ -594,192 +541,4 @@ namespace EMotionFX renderInfo->m_renderUtil->RenderArrow(0.1f, jointChildWorldSpaceTransformNoScale.m_position, MCore::GetRight(jointChildWorldSpaceTransformNoScale.ToAZTransform()), color); } - - void RagdollNodeInspectorPlugin::Render(EMotionFX::ActorRenderFlagBitset renderFlags) - { - const bool renderColliders = renderFlags[RENDER_RAGDOLL_COLLIDERS]; - const bool renderJointLimits = renderFlags[RENDER_RAGDOLL_JOINTLIMITS]; - if (!renderColliders && !renderJointLimits) - { - return; - } - - const size_t actorInstanceCount = GetActorManager().GetNumActorInstances(); - for (size_t i = 0; i < actorInstanceCount; ++i) - { - ActorInstance* actorInstance = GetActorManager().GetActorInstance(i); - RenderRagdoll(actorInstance, renderColliders, renderJointLimits); - } - } - - void RagdollNodeInspectorPlugin::RenderRagdoll( - ActorInstance* actorInstance, - bool renderColliders, - bool renderJointLimits) - { - const Actor* actor = actorInstance->GetActor(); - const Skeleton* skeleton = actor->GetSkeleton(); - const size_t numNodes = skeleton->GetNumNodes(); - const AZStd::shared_ptr& physicsSetup = actor->GetPhysicsSetup(); - const Physics::RagdollConfiguration& ragdollConfig = physicsSetup->GetRagdollConfig(); - const AZStd::vector& ragdollNodes = ragdollConfig.m_nodes; - const Physics::CharacterColliderConfiguration& colliderConfig = ragdollConfig.m_colliders; - const RagdollInstance* ragdollInstance = actorInstance->GetRagdollInstance(); - - const AZ::Render::RenderActorSettings& settings = EMotionFX::GetRenderActorSettings(); - const AZ::Color& violatedColor = settings.m_violatedJointLimitColor; - const AZ::Color& defaultColor = settings.m_ragdollColliderColor; - const AZ::Color& selectedColor = settings.m_selectedRagdollColliderColor; - - const AZStd::unordered_set& selectedJointIndices = EMStudio::GetManager()->GetSelectedJointIndices(); - - for (size_t nodeIndex = 0; nodeIndex < numNodes; ++nodeIndex) - { - const Node* joint = skeleton->GetNode(nodeIndex); - const size_t jointIndex = joint->GetNodeIndex(); - - AZ::Outcome ragdollNodeIndex = AZ::Failure(); - if (ragdollInstance) - { - ragdollNodeIndex = ragdollInstance->GetRagdollNodeIndex(jointIndex); - } - else - { - ragdollNodeIndex = ragdollConfig.FindNodeConfigIndexByName(joint->GetNameString()); - } - - if (!ragdollNodeIndex.IsSuccess()) - { - continue; - } - - const bool jointSelected = selectedJointIndices.empty() || selectedJointIndices.find(jointIndex) != selectedJointIndices.end(); - - AZ::Color finalColor; - if (jointSelected) - { - finalColor = selectedColor; - } - else - { - finalColor = defaultColor; - } - - const Physics::RagdollNodeConfiguration& ragdollNode = ragdollNodes[ragdollNodeIndex.GetValue()]; - - if (renderColliders) - { - const Physics::CharacterColliderNodeConfiguration* colliderNodeConfig = - colliderConfig.FindNodeConfigByName(joint->GetNameString()); - if (colliderNodeConfig) - { - const AzPhysics::ShapeColliderPairList& colliders = colliderNodeConfig->m_shapes; - ColliderContainerWidget::RenderColliders(colliders, actorInstance, joint, finalColor); - } - } - - if (renderJointLimits && jointSelected) - { - const AZStd::shared_ptr& jointLimitConfig = ragdollNode.m_jointConfig; - if (jointLimitConfig) - { - const Node* ragdollParentNode = physicsSetup->FindRagdollParentNode(joint); - if (ragdollParentNode) - { - RenderJointLimit(*jointLimitConfig, actorInstance, joint, ragdollParentNode, finalColor, violatedColor); - RenderJointFrame(*jointLimitConfig, actorInstance, joint, ragdollParentNode, finalColor); - } - } - } - } - } - - void RagdollNodeInspectorPlugin::RenderJointLimit( - const AzPhysics::JointConfiguration& configuration, - const ActorInstance* actorInstance, - const Node* node, - const Node* parentNode, - const AZ::Color& regularColor, - const AZ::Color& violatedColor) - { - const size_t nodeIndex = node->GetNodeIndex(); - const size_t parentNodeIndex = parentNode->GetNodeIndex(); - const Transform& actorInstanceWorldTransform = actorInstance->GetWorldSpaceTransform(); - const Pose* currentPose = actorInstance->GetTransformData()->GetCurrentPose(); - const AZ::Quaternion& parentOrientation = currentPose->GetModelSpaceTransform(parentNodeIndex).m_rotation; - const AZ::Quaternion& childOrientation = currentPose->GetModelSpaceTransform(nodeIndex).m_rotation; - - m_vertexBuffer.clear(); - m_indexBuffer.clear(); - m_lineBuffer.clear(); - m_lineValidityBuffer.clear(); - if (auto* jointHelpers = AZ::Interface::Get()) - { - jointHelpers->GenerateJointLimitVisualizationData( - configuration, parentOrientation, childOrientation, s_scale, s_angularSubdivisions, s_radialSubdivisions, m_vertexBuffer, - m_indexBuffer, m_lineBuffer, m_lineValidityBuffer); - } - - Transform jointModelSpaceTransform = currentPose->GetModelSpaceTransform(parentNodeIndex); - jointModelSpaceTransform.m_position = currentPose->GetModelSpaceTransform(nodeIndex).m_position; - const Transform jointGlobalTransformNoScale = jointModelSpaceTransform * actorInstanceWorldTransform; - - const size_t numLineBufferEntries = m_lineBuffer.size(); - if (m_lineValidityBuffer.size() * 2 != numLineBufferEntries) - { - AZ_ErrorOnce("EMotionFX", false, "Unexpected buffer size in joint limit visualization for node %s", node->GetName()); - return; - } - - AZ::s32 viewportId = -1; - EMStudio::ViewportPluginRequestBus::BroadcastResult(viewportId, &EMStudio::ViewportPluginRequestBus::Events::GetViewportId); - AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; - AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, viewportId); - AzFramework::DebugDisplayRequests* debugDisplay = nullptr; - debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); - if (!debugDisplay) - { - return; - } - - for (size_t i = 0; i < numLineBufferEntries; i += 2) - { - const AZ::Color& lineColor = m_lineValidityBuffer[i / 2] ? regularColor : violatedColor; - debugDisplay->DrawLine( - jointGlobalTransformNoScale.TransformPoint(m_lineBuffer[i]), - jointGlobalTransformNoScale.TransformPoint(m_lineBuffer[i + 1]), lineColor.GetAsVector4(), lineColor.GetAsVector4() - ); - } - } - - void RagdollNodeInspectorPlugin::RenderJointFrame( - const AzPhysics::JointConfiguration& configuration, - const ActorInstance* actorInstance, - const Node* node, - const Node* parentNode, - const AZ::Color& color) - { - AZ_UNUSED(parentNode); - - const Transform& actorInstanceWorldSpaceTransform = actorInstance->GetWorldSpaceTransform(); - const Pose* currentPose = actorInstance->GetTransformData()->GetCurrentPose(); - const Transform childJointLocalSpaceTransform(AZ::Vector3::CreateZero(), configuration.m_childLocalRotation); - const Transform childModelSpaceTransform = - childJointLocalSpaceTransform * currentPose->GetModelSpaceTransform(node->GetNodeIndex()); - const Transform jointChildWorldSpaceTransformNoScale = (childModelSpaceTransform * actorInstanceWorldSpaceTransform); - AZ::Vector3 dir = jointChildWorldSpaceTransformNoScale.ToAZTransform().GetBasisX(); - - AZ::s32 viewportId = -1; - EMStudio::ViewportPluginRequestBus::BroadcastResult(viewportId, &EMStudio::ViewportPluginRequestBus::Events::GetViewportId); - AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; - AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, viewportId); - AzFramework::DebugDisplayRequests* debugDisplay = nullptr; - debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); - if (!debugDisplay) - { - return; - } - debugDisplay->SetColor(color); - debugDisplay->DrawArrow(jointChildWorldSpaceTransformNoScale.m_position, jointChildWorldSpaceTransformNoScale.m_position + dir, 0.1f); - } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h index d3b27da5ce..7885b8bacd 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h @@ -57,14 +57,6 @@ namespace EMotionFX //! Deprecated: All legacy render function is tied to openGL. Will be removed after openGLPlugin is completely removed. void LegacyRender(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) override; void LegacyRenderRagdoll(ActorInstance* actorInstance, bool renderColliders, bool renderJointLimits, EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo); - void LegacyRenderJointLimit( - const AzPhysics::JointConfiguration& jointConfiguration, - const ActorInstance* actorInstance, - const Node* node, - const Node* parentNode, - EMStudio::RenderPlugin* renderPlugin, - EMStudio::EMStudioPlugin::RenderInfo* renderInfo, - const MCore::RGBAColor& color); void LegacyRenderJointFrame( const AzPhysics::JointConfiguration& jointConfiguration, const ActorInstance* actorInstance, @@ -73,26 +65,6 @@ namespace EMotionFX EMStudio::EMStudioPlugin::RenderInfo* renderInfo, const MCore::RGBAColor& color); - //! Those function replaces legacyRender function and calls atom auxGeom render internally. - void Render(EMotionFX::ActorRenderFlagBitset renderFlags) override; - void RenderRagdoll( - ActorInstance* actorInstance, - bool renderColliders, - bool renderJointLimits); - void RenderJointLimit( - const AzPhysics::JointConfiguration& jointConfiguration, - const ActorInstance* actorInstance, - const Node* node, - const Node* parentNode, - const AZ::Color& regularColor, - const AZ::Color& violatedColor); - void RenderJointFrame( - const AzPhysics::JointConfiguration& jointConfiguration, - const ActorInstance* actorInstance, - const Node* node, - const Node* parentNode, - const AZ::Color& color); - public slots: void OnAddToRagdoll(); void OnAddCollider(); @@ -103,14 +75,6 @@ namespace EMotionFX private: bool IsPhysXGemAvailable() const; - RagdollNodeWidget* m_nodeWidget; - - static float s_scale; - static AZ::u32 s_angularSubdivisions; - static AZ::u32 s_radialSubdivisions; - AZStd::vector m_vertexBuffer; - AZStd::vector m_indexBuffer; - AZStd::vector m_lineBuffer; - AZStd::vector m_lineValidityBuffer; + RagdollNodeWidget* m_nodeWidget; }; } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.cpp index 695603cecb..bb7b06a5dc 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.cpp @@ -570,17 +570,15 @@ namespace EMotionFX drawData->Unlock(); } - void SimulatedObjectWidget::Render(EMotionFX::ActorRenderFlagBitset renderFlags) + void SimulatedObjectWidget::Render(EMotionFX::ActorRenderFlags renderFlags) { if (!m_actor || !m_actorInstance) { return; } - const AZ::Render::RenderActorSettings& settings = EMotionFX::GetRenderActorSettings(); - const bool renderSimulatedJoints = renderFlags[RENDER_SIMULATEJOINTS]; const AZStd::unordered_set& selectedJointIndices = EMStudio::GetManager()->GetSelectedJointIndices(); - if (renderSimulatedJoints && !selectedJointIndices.empty()) + if (AZ::RHI::CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::SimulatedJoints) && !selectedJointIndices.empty()) { // Render the joint radius. const size_t actorInstanceCount = GetActorManager().GetNumActorInstances(); @@ -612,13 +610,6 @@ namespace EMotionFX } } } - - const bool renderColliders = renderFlags[RENDER_SIMULATEDOBJECT_COLLIDERS]; - if (renderColliders) - { - ColliderContainerWidget::RenderColliders(PhysicsSetup::SimulatedObjectCollider, - settings.m_simulatedObjectColliderColor, settings.m_selectedSimulatedObjectColliderColor); - } } void SimulatedObjectWidget::RenderJointRadius(const SimulatedJoint* joint, ActorInstance* actorInstance, const AZ::Color& color) diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.h b/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.h index 9662fbc150..a3a6344a09 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.h +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.h @@ -61,7 +61,7 @@ namespace EMotionFX void LegacyRender(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) override; void LegacyRenderJointRadius(const SimulatedJoint* joint, ActorInstance* actorInstance, const AZ::Color& color); - void Render(EMotionFX::ActorRenderFlagBitset renderFlags) override; + void Render(EMotionFX::ActorRenderFlags renderFlags) override; void RenderJointRadius(const SimulatedJoint* joint, ActorInstance* actorInstance, const AZ::Color& color); SimulatedObjectModel* GetSimulatedObjectModel() const; diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp index ec8d1147f2..79ed15c9a6 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.cpp @@ -16,11 +16,8 @@ #include #include - namespace EMotionFX { - int SkeletonOutlinerPlugin::s_iconSize = 16; - SkeletonOutlinerPlugin::SkeletonOutlinerPlugin() : EMStudio::DockWidgetPlugin() , m_mainWidget(nullptr) @@ -30,13 +27,19 @@ namespace EMotionFX SkeletonOutlinerPlugin::~SkeletonOutlinerPlugin() { + // Reset selection on close. + if (m_skeletonModel) + { + m_skeletonModel->GetSelectionModel().clearSelection(); + m_skeletonModel.reset(); + } + for (MCore::Command::Callback* callback : m_commandCallbacks) { CommandSystem::GetCommandManager()->RemoveCommandCallback(callback, true); } m_commandCallbacks.clear(); - SkeletonOutlinerNotificationBus::Broadcast(&SkeletonOutlinerNotifications::SingleNodeSelectionChanged, nullptr, nullptr); EMotionFX::SkeletonOutlinerRequestBus::Handler::BusDisconnect(); } diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.h b/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.h index bdb4b24ab3..e28e275422 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.h +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.h @@ -19,7 +19,6 @@ #include #endif - QT_FORWARD_DECLARE_CLASS(QLabel) namespace EMotionFX @@ -73,7 +72,7 @@ namespace EMotionFX AZStd::unique_ptr m_skeletonModel; QModelIndexList m_selectedRows; SkeletonSortFilterProxyModel* m_filterProxyModel; - static int s_iconSize; + static constexpr int s_iconSize = 16; // Callbacks // Works for all commands that use the actor id as well as the joint name mixins diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp index e38e15d124..a44882694f 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -131,7 +132,10 @@ namespace EMotionFX return AZ::Edit::PropertyVisibility::Show; } + ////////////////////////////////////////////////////////////////////////// + AZ_ENUM_DEFINE_REFLECT_UTILITIES(ActorRenderFlags); + void ActorComponent::Configuration::Reflect(AZ::ReflectContext* context) { BoundingBoxConfiguration::Reflect(context); @@ -139,19 +143,19 @@ namespace EMotionFX auto* serializeContext = azrtti_cast(context); if (serializeContext) { + ActorRenderFlagsReflect(*serializeContext); + serializeContext->Class() - ->Version(4) + ->Version(5) ->Field("ActorAsset", &Configuration::m_actorAsset) ->Field("MaterialPerLOD", &Configuration::m_materialPerLOD) - ->Field("RenderSkeleton", &Configuration::m_renderSkeleton) - ->Field("RenderCharacter", &Configuration::m_renderCharacter) - ->Field("RenderBounds", &Configuration::m_renderBounds) ->Field("AttachmentType", &Configuration::m_attachmentType) ->Field("AttachmentTarget", &Configuration::m_attachmentTarget) ->Field("SkinningMethod", &Configuration::m_skinningMethod) ->Field("LODLevel", &Configuration::m_lodLevel) ->Field("BoundingBoxConfig", &Configuration::m_bboxConfig) ->Field("ForceJointsUpdateOOV", &Configuration::m_forceUpdateJointsOOV) + ->Field("RenderFlags", &Configuration::m_renderFlags) ; } } @@ -250,8 +254,6 @@ namespace EMotionFX { m_configuration = *configuration; } - - m_debugRenderFlags[RENDER_SOLID] = true; } ////////////////////////////////////////////////////////////////////////// @@ -344,20 +346,24 @@ namespace EMotionFX ////////////////////////////////////////////////////////////////////////// bool ActorComponent::GetRenderCharacter() const { - return m_configuration.m_renderCharacter; + return AZ::RHI::CheckBitsAny(m_configuration.m_renderFlags, ActorRenderFlags::Solid); } ////////////////////////////////////////////////////////////////////////// void ActorComponent::SetRenderCharacter(bool enable) { - if (m_configuration.m_renderCharacter != enable) + if (enable) + { + m_configuration.m_renderFlags |= ActorRenderFlags::Solid; + } + else { - m_configuration.m_renderCharacter = enable; + m_configuration.m_renderFlags &= ~ActorRenderFlags::Solid; + } - if (m_renderActorInstance) - { - m_renderActorInstance->SetIsVisible(m_configuration.m_renderCharacter); - } + if (m_renderActorInstance) + { + m_renderActorInstance->SetIsVisible(enable); } } @@ -394,9 +400,9 @@ namespace EMotionFX return m_sceneFinishSimHandler.IsConnected(); } - void ActorComponent::SetRenderFlag(ActorRenderFlagBitset renderFlags) + void ActorComponent::SetRenderFlag(ActorRenderFlags renderFlags) { - m_debugRenderFlags = renderFlags; + m_configuration.m_renderFlags = renderFlags; } void ActorComponent::CheckActorCreation() @@ -456,7 +462,7 @@ namespace EMotionFX if (m_renderActorInstance) { - m_renderActorInstance->SetIsVisible(m_configuration.m_renderCharacter); + m_renderActorInstance->SetIsVisible(AZ::RHI::CheckBitsAny(m_configuration.m_renderFlags, ActorRenderFlags::Solid)); } } @@ -563,22 +569,18 @@ namespace EMotionFX m_renderActorInstance->OnTick(deltaTime); m_renderActorInstance->UpdateBounds(); AZ::Interface::Get()->RefreshEntityLocalBoundsUnion(GetEntityId()); + const bool renderActorSolid = AZ::RHI::CheckBitsAny(m_configuration.m_renderFlags, ActorRenderFlags::Solid); // Optimization: Set the actor instance invisible when character is out of camera view. This will stop the joint transforms update, except the root joint. // Calling it after the bounds on the render actor updated. if (!m_configuration.m_forceUpdateJointsOOV) { const bool isInCameraFrustum = m_renderActorInstance->IsInCameraFrustum(); - m_actorInstance->SetIsVisible(isInCameraFrustum && m_configuration.m_renderCharacter); + m_actorInstance->SetIsVisible(isInCameraFrustum && renderActorSolid); } - m_renderActorInstance->SetIsVisible(m_debugRenderFlags[RENDER_SOLID]); - - // The configuration stores some debug option. When that is enabled, we override it on top of the render flags. - m_debugRenderFlags[RENDER_AABB] = m_debugRenderFlags[RENDER_AABB] || m_configuration.m_renderBounds; - m_debugRenderFlags[RENDER_LINESKELETON] = m_debugRenderFlags[RENDER_LINESKELETON] || m_configuration.m_renderSkeleton; - m_debugRenderFlags[RENDER_EMFX_DEBUG] = true; - m_renderActorInstance->DebugDraw(m_debugRenderFlags); + m_renderActorInstance->SetIsVisible(renderActorSolid); + m_renderActorInstance->DebugDraw(m_configuration.m_renderFlags); } } diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h index 9eecea2f54..9bf2b88325 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Components/ActorComponent.h @@ -82,11 +82,9 @@ namespace EMotionFX AZ::EntityId m_attachmentTarget{}; ///< Target entity this actor should attach to. size_t m_attachmentJointIndex = InvalidIndex; ///< Index of joint on target skeleton for actor attachments. AttachmentType m_attachmentType = AttachmentType::None; ///< Type of attachment. - bool m_renderSkeleton = false; ///< Toggles debug rendering of the skeleton. - bool m_renderCharacter = true; ///< Toggles rendering of the character. - bool m_renderBounds = false; ///< Toggles rendering of the character bounds used for visibility testing. SkinningMethod m_skinningMethod = SkinningMethod::DualQuat; ///< The skinning method for this actor size_t m_lodLevel = 0; + ActorRenderFlags m_renderFlags = ActorRenderFlags::Default; ///< Actor render flag // Force updating the joints when it is out of camera view. By // default, joints level update (beside the root joint) on @@ -180,7 +178,7 @@ namespace EMotionFX bool IsPhysicsSceneSimulationFinishEventConnected() const; AZ::Data::Asset GetActorAsset() const { return m_configuration.m_actorAsset; } - void SetRenderFlag(ActorRenderFlagBitset renderFlags); + void SetRenderFlag(ActorRenderFlags renderFlags); private: // AZ::TransformNotificationBus::MultiHandler @@ -202,7 +200,6 @@ namespace EMotionFX AZStd::vector m_attachments; AZStd::unique_ptr m_renderActorInstance; - ActorRenderFlagBitset m_debugRenderFlags; ///< Actor debug render flag AzPhysics::SceneEvents::OnSceneSimulationFinishHandler m_sceneFinishSimHandler; }; diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index 4292f04a08..3139e97b16 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -124,12 +124,12 @@ namespace EMotionFX ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(0, &EditorActorComponent::m_renderCharacter, "Draw character", "Toggles rendering of character mesh.") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorActorComponent::OnDebugDrawFlagChanged) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorActorComponent::OnRenderFlagChanged) ->DataElement(0, &EditorActorComponent::m_renderSkeleton, "Draw skeleton", "Toggles rendering of skeleton.") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorActorComponent::OnDebugDrawFlagChanged) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorActorComponent::OnRenderFlagChanged) ->DataElement(0, &EditorActorComponent::m_renderBounds, "Draw bounds", "Toggles rendering of world space bounding boxes.") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorActorComponent::OnDebugDrawFlagChanged) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorActorComponent::OnRenderFlagChanged) ->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorActorComponent::m_skinningMethod, "Skinning method", "Choose the skinning method this actor is using") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorActorComponent::OnSkinningMethodChanged) @@ -180,7 +180,6 @@ namespace EMotionFX , m_lodLevel(0) , m_actorAsset(AZ::Data::AssetLoadBehavior::NoLoad) { - m_debugRenderFlags[RENDER_SOLID] = true; } ////////////////////////////////////////////////////////////////////////// @@ -198,6 +197,7 @@ namespace EMotionFX { AzToolsFramework::Components::EditorComponentBase::Activate(); + UpdateRenderFlags(); LoadActorAsset(); const AZ::EntityId entityId = GetEntityId(); @@ -363,8 +363,9 @@ namespace EMotionFX } ////////////////////////////////////////////////////////////////////////// - void EditorActorComponent::OnDebugDrawFlagChanged() + void EditorActorComponent::OnRenderFlagChanged() { + UpdateRenderFlags(); if (m_renderSkeleton || m_renderBounds || m_renderCharacter) { AZ::TickBus::Handler::BusConnect(); @@ -573,6 +574,23 @@ namespace EMotionFX ToolsApplicationEvents::Bus::Broadcast(&ToolsApplicationEvents::InvalidatePropertyDisplay, Refresh_EntireTree); } + void EditorActorComponent::UpdateRenderFlags() + { + m_renderFlags = ActorRenderFlags::None; + if (m_renderCharacter) + { + m_renderFlags |= ActorRenderFlags::Solid; + } + if (m_renderBounds) + { + m_renderFlags |= ActorRenderFlags::AABB; + } + if (m_renderSkeleton) + { + m_renderFlags |= ActorRenderFlags::LineSkeleton; + } + } + ////////////////////////////////////////////////////////////////////////// void EditorActorComponent::OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) { @@ -616,22 +634,16 @@ namespace EMotionFX { m_renderActorInstance->OnTick(deltaTime); m_renderActorInstance->UpdateBounds(); - - m_debugRenderFlags[RENDER_AABB] = m_renderBounds; - m_debugRenderFlags[RENDER_LINESKELETON] = m_renderSkeleton; - m_debugRenderFlags[RENDER_EMFX_DEBUG] = true; - m_renderActorInstance->DebugDraw(m_debugRenderFlags); + m_renderActorInstance->DebugDraw(m_renderFlags); } } void EditorActorComponent::BuildGameEntity(AZ::Entity* gameEntity) { + UpdateRenderFlags(); ActorComponent::Configuration cfg; cfg.m_actorAsset = m_actorAsset; cfg.m_materialPerLOD = m_materialPerLOD; - cfg.m_renderSkeleton = m_renderSkeleton; - cfg.m_renderCharacter = m_renderCharacter; - cfg.m_renderBounds = m_renderBounds; cfg.m_attachmentType = m_attachmentType; cfg.m_attachmentTarget = m_attachmentTarget; cfg.m_attachmentJointIndex = m_attachmentJointIndex; @@ -639,6 +651,7 @@ namespace EMotionFX cfg.m_skinningMethod = m_skinningMethod; cfg.m_bboxConfig = m_bboxConfig; cfg.m_forceUpdateJointsOOV = m_forceUpdateJointsOOV; + cfg.m_renderFlags = m_renderFlags; gameEntity->AddComponent(aznew ActorComponent(&cfg)); } @@ -861,7 +874,7 @@ namespace EMotionFX void EditorActorComponent::CheckActorCreation() { // Enable/disable debug drawing. - OnDebugDrawFlagChanged(); + OnRenderFlagChanged(); // Create actor instance. auto* actorAsset = m_actorAsset.GetAs(); @@ -964,9 +977,9 @@ namespace EMotionFX } } - void EditorActorComponent::SetRenderFlag(ActorRenderFlagBitset renderFlags) + void EditorActorComponent::SetRenderFlag(ActorRenderFlags renderFlags) { - m_debugRenderFlags = renderFlags; + m_renderFlags = renderFlags; } } //namespace Integration } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h index f4c663a92f..0336a0844d 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.h @@ -104,7 +104,7 @@ namespace EMotionFX ActorComponent::GetRequiredServices(required); } - void SetRenderFlag(ActorRenderFlagBitset renderFlags); + void SetRenderFlag(ActorRenderFlags renderFlags); static void Reflect(AZ::ReflectContext* context); @@ -114,7 +114,7 @@ namespace EMotionFX void OnMaterialChanged(); void OnMaterialPerActorChanged(); void OnLODLevelChanged(); - void OnDebugDrawFlagChanged(); + void OnRenderFlagChanged(); void OnSkinningMethodChanged(); AZ::Crc32 OnAttachmentTypeChanged(); AZ::Crc32 OnAttachmentTargetChanged(); @@ -124,6 +124,7 @@ namespace EMotionFX bool AttachmentTargetJointVisibility(); AZStd::string AttachmentJointButtonText(); void InitializeMaterial(ActorAsset& actorAsset); + void UpdateRenderFlags(); void LaunchAnimationEditor(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType&); @@ -164,7 +165,7 @@ namespace EMotionFX size_t m_lodLevel; ActorComponent::BoundingBoxConfiguration m_bboxConfig; bool m_forceUpdateJointsOOV = false; - ActorRenderFlagBitset m_debugRenderFlags; ///< Actor debug render flag + ActorRenderFlags m_renderFlags; ///< Actor render flag // \todo attachmentTarget node nr // Note: LOD work in progress. For now we use one material instead of a list of material, because we don't have the support for LOD with multiple scene files. diff --git a/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorInstance.h b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorInstance.h index 8afb2a2f9a..f124f352c6 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorInstance.h +++ b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorInstance.h @@ -34,7 +34,7 @@ namespace EMotionFX virtual ~RenderActorInstance() = default; virtual void OnTick(float timeDelta) = 0; - virtual void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags) = 0; + virtual void DebugDraw(const EMotionFX::ActorRenderFlags& renderFlags) = 0; SkinningMethod GetSkinningMethod() const; virtual void SetSkinningMethod(SkinningMethod skinningMethod); diff --git a/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderFlag.h b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderFlag.h index e053eae6d5..4640fdfcb1 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderFlag.h +++ b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderFlag.h @@ -7,38 +7,87 @@ */ #pragma once -#include +#include +#include namespace EMotionFX { - enum ActorRenderFlag + // The index of the render flag which is 0, 1, 2, 3.. based. + // Do not confuse this with the actual ActorRenderFlags::Type which is 1, 2, 4, 8.. based. + enum ActorRenderFlagIndex : AZ::u8 { - RENDER_SOLID = 0, - RENDER_WIREFRAME = 1, - RENDER_LIGHTING = 2, - RENDER_SHADOWS = 3, - RENDER_FACENORMALS = 4, - RENDER_VERTEXNORMALS = 5, - RENDER_TANGENTS = 6, - RENDER_AABB = 7, - RENDER_SKELETON = 8, - RENDER_LINESKELETON = 9, - RENDER_NODEORIENTATION = 10, - RENDER_NODENAMES = 11, - RENDER_GRID = 12, - RENDER_BACKFACECULLING = 13, - RENDER_ACTORBINDPOSE = 14, - RENDER_RAGDOLL_COLLIDERS = 15, - RENDER_RAGDOLL_JOINTLIMITS = 16, - RENDER_HITDETECTION_COLLIDERS = 17, - RENDER_USE_GRADIENTBACKGROUND = 18, - RENDER_MOTIONEXTRACTION = 19, - RENDER_CLOTH_COLLIDERS = 20, - RENDER_SIMULATEDOBJECT_COLLIDERS = 21, - RENDER_SIMULATEJOINTS = 22, - RENDER_EMFX_DEBUG = 23, - NUM_RENDERFLAGS = 24 + SOLID = 0, + WIREFRAME = 1, + LIGHTING = 2, + SHADOWS = 3, + FACENORMALS = 4, + VERTEXNORMALS = 5, + TANGENTS = 6, + AABB = 7, + SKELETON = 8, + LINESKELETON = 9, + NODEORIENTATION = 10, + NODENAMES = 11, + GRID = 12, + BACKFACECULLING = 13, + ACTORBINDPOSE = 14, + RAGDOLL_COLLIDERS = 15, + RAGDOLL_JOINTLIMITS = 16, + HITDETECTION_COLLIDERS = 17, + USE_GRADIENTBACKGROUND = 18, + MOTIONEXTRACTION = 19, + CLOTH_COLLIDERS = 20, + SIMULATEDOBJECT_COLLIDERS = 21, + SIMULATEJOINTS = 22, + EMFX_DEBUG = 23, + NUM_RENDERFLAGINDEXES = 24 }; - using ActorRenderFlagBitset = AZStd::bitset; + //! A set of combinable flags which indicate which render option in turned on for the actor. + AZ_ENUM_CLASS_WITH_UNDERLYING_TYPE(ActorRenderFlags, AZ::u32, + (None, 0), + (Solid, AZ_BIT(ActorRenderFlagIndex::SOLID)), + (Wireframe, AZ_BIT(ActorRenderFlagIndex::WIREFRAME)), + (Lighting, AZ_BIT(ActorRenderFlagIndex::LIGHTING)), + (Default, Solid | Lighting), + (Shadows, AZ_BIT(ActorRenderFlagIndex::SHADOWS)), + (FaceNormals, AZ_BIT(ActorRenderFlagIndex::FACENORMALS)), + (VertexNormals, AZ_BIT(ActorRenderFlagIndex::VERTEXNORMALS)), + (Tangents, AZ_BIT(ActorRenderFlagIndex::TANGENTS)), + (AABB, AZ_BIT(ActorRenderFlagIndex::AABB)), + (Skeleton, AZ_BIT(ActorRenderFlagIndex::SKELETON)), + (LineSkeleton, AZ_BIT(ActorRenderFlagIndex::LINESKELETON)), + (NodeOrientation, AZ_BIT(ActorRenderFlagIndex::NODEORIENTATION)), + (NodeNames, AZ_BIT(ActorRenderFlagIndex::NODENAMES)), + (Grid, AZ_BIT(ActorRenderFlagIndex::GRID)), + (BackfaceCulling, AZ_BIT(ActorRenderFlagIndex::BACKFACECULLING)), + (ActorBindPose, AZ_BIT(ActorRenderFlagIndex::ACTORBINDPOSE)), + (RagdollColliders, AZ_BIT(ActorRenderFlagIndex::RAGDOLL_COLLIDERS)), + (RagdollJointLimits, AZ_BIT(ActorRenderFlagIndex::RAGDOLL_JOINTLIMITS)), + (HitDetectionColliders, AZ_BIT(ActorRenderFlagIndex::HITDETECTION_COLLIDERS)), + (UseGradientBackground, AZ_BIT(ActorRenderFlagIndex::USE_GRADIENTBACKGROUND)), + (MotionExtraction, AZ_BIT(ActorRenderFlagIndex::MOTIONEXTRACTION)), + (ClothColliders, AZ_BIT(ActorRenderFlagIndex::CLOTH_COLLIDERS)), + (SimulatedObjectColliders, AZ_BIT(ActorRenderFlagIndex::SIMULATEDOBJECT_COLLIDERS)), + (SimulatedJoints, AZ_BIT(ActorRenderFlagIndex::SIMULATEJOINTS)), + (EmfxDebug, AZ_BIT(ActorRenderFlagIndex::EMFX_DEBUG)) + ); + + AZ_DEFINE_ENUM_BITWISE_OPERATORS(ActorRenderFlags); + + class ActorRenderFlagUtil + { + public: + // Check the bit value with the offset start at 0 from the right. + // CheckBit(flags, 0) means check the last digit of the flags, CheckBit(flags, 1) means the second digit from right, etc. + static bool CheckBit(ActorRenderFlags flags, AZ::u8 offset) + { + return (flags & ActorRenderFlags(AZ_BIT(offset))) != ActorRenderFlags(0); + } + }; +} + +namespace AZ +{ + AZ_TYPE_INFO_SPECIALIZE(EMotionFX::ActorRenderFlags, "{2D2187FA-2C1A-4485-AF7C-AD34C0514105}"); } diff --git a/Gems/EMotionFX/Code/Tests/RenderBackendManagerTests.cpp b/Gems/EMotionFX/Code/Tests/RenderBackendManagerTests.cpp index be990f444c..e1e092cf3d 100644 --- a/Gems/EMotionFX/Code/Tests/RenderBackendManagerTests.cpp +++ b/Gems/EMotionFX/Code/Tests/RenderBackendManagerTests.cpp @@ -65,7 +65,7 @@ namespace EMotionFX } MOCK_METHOD1(OnTick, void(float)); - MOCK_METHOD1(DebugDraw, void(const EMotionFX::ActorRenderFlagBitset&)); + MOCK_METHOD1(DebugDraw, void(const EMotionFX::ActorRenderFlags&)); MOCK_CONST_METHOD0(IsVisible, bool()); MOCK_METHOD1(SetIsVisible, void(bool)); MOCK_METHOD1(SetMaterials, void(const ActorAsset::MaterialList&)); diff --git a/Gems/FastNoise/Code/Source/EditorFastNoiseGradientComponent.h b/Gems/FastNoise/Code/Source/EditorFastNoiseGradientComponent.h index 354fe18b03..e0b0a647e5 100644 --- a/Gems/FastNoise/Code/Source/EditorFastNoiseGradientComponent.h +++ b/Gems/FastNoise/Code/Source/EditorFastNoiseGradientComponent.h @@ -28,7 +28,7 @@ namespace FastNoiseGem static constexpr const char* const s_componentDescription = "Generates gradient values using FastNoise a noise generation library with a collection of realtime noise algorithms"; static constexpr const char* const s_icon = "Editor/Icons/Components/Gradient.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/Gradient.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = ""; private: AZ::Crc32 OnGenerateRandomSeed(); diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h index 7ec1c785e5..ca1de2cdc1 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/ImageGradientComponent.h @@ -99,6 +99,7 @@ namespace GradientSignal void SetupDependencies(); void GetSubImageData(); + float GetValueFromImageData(const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue) const; // ImageGradientRequestBus overrides... AZStd::string GetImageAssetPath() const override; diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/SurfaceAltitudeGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/SurfaceAltitudeGradientComponent.h index d8e902a0d5..5632d17095 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/SurfaceAltitudeGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/SurfaceAltitudeGradientComponent.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -107,21 +108,6 @@ namespace GradientSignal void AddTag(AZStd::string tag) override; private: - static float CalculateAltitudeRatio(const SurfaceData::SurfacePointList& points, float altitudeMin, float altitudeMax) - { - if (points.IsEmpty()) - { - return 0.0f; - } - - // GetSurfacePoints (which was used to populate the points list) always returns points in decreasing height order, so the - // first point in the list contains the highest altitude. - const float highestAltitude = points.GetHighestSurfacePoint().m_position.GetZ(); - - // Turn the absolute altitude value into a 0-1 value by returning the % of the given altitude range that it falls at. - return GetRatio(altitudeMin, altitudeMax, highestAltitude); - } - mutable AZStd::shared_mutex m_cacheMutex; SurfaceAltitudeGradientConfig m_configuration; LmbrCentral::DependencyMonitor m_dependencyMonitor; diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/SurfaceMaskGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/SurfaceMaskGradientComponent.h index eb009e6b92..2571ce8ab1 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/SurfaceMaskGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/SurfaceMaskGradientComponent.h @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace LmbrCentral @@ -81,26 +82,6 @@ namespace GradientSignal void AddTag(AZStd::string tag) override; private: - static float GetMaxSurfaceWeight(const SurfaceData::SurfacePointList& points) - { - float result = 0.0f; - - points.EnumeratePoints([&result]( - [[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& normal, - const SurfaceData::SurfaceTagWeights& masks) -> bool - { - masks.EnumerateWeights( - [&result]([[maybe_unused]] AZ::Crc32 surfaceType, float weight) -> bool - { - result = AZ::GetMax(AZ::GetClamp(weight, 0.0f, 1.0f), result); - return true; - }); - return true; - }); - - return result; - } - SurfaceMaskGradientConfig m_configuration; LmbrCentral::DependencyMonitor m_dependencyMonitor; }; diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Components/SurfaceSlopeGradientComponent.h b/Gems/GradientSignal/Code/Include/GradientSignal/Components/SurfaceSlopeGradientComponent.h index e7a55a2bbc..d7b24e1928 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Components/SurfaceSlopeGradientComponent.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Components/SurfaceSlopeGradientComponent.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -123,37 +124,6 @@ namespace GradientSignal void SetFallOffMidpoint(float midpoint) override; private: - float GetSlopeRatio(const SurfaceData::SurfacePointList& points, float angleMin, float angleMax) const - { - if (points.IsEmpty()) - { - return 0.0f; - } - - // Assuming our surface normal vector is actually normalized, we can get the slope - // by just grabbing the Z value. It's the same thing as normal.Dot(AZ::Vector3::CreateAxisZ()). - AZ_Assert( - points.GetHighestSurfacePoint().m_normal.GetNormalized().IsClose(points.GetHighestSurfacePoint().m_normal), - "Surface normals are expected to be normalized"); - const float slope = points.GetHighestSurfacePoint().m_normal.GetZ(); - // Convert slope back to an angle so that we can lerp in "angular space", not "slope value space". - // (We want our 0-1 range to be linear across the range of angles) - const float slopeAngle = acosf(slope); - - switch (m_configuration.m_rampType) - { - case SurfaceSlopeGradientConfig::RampType::SMOOTH_STEP: - return m_configuration.m_smoothStep.GetSmoothedValue(GetRatio(angleMin, angleMax, slopeAngle)); - case SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_UP: - // For ramp up, linearly interpolate from min to max. - return GetRatio(angleMin, angleMax, slopeAngle); - case SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_DOWN: - default: - // For ramp down, linearly interpolate from max to min. - return GetRatio(angleMax, angleMin, slopeAngle); - } - } - SurfaceSlopeGradientConfig m_configuration; }; } diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/ImageAsset.h b/Gems/GradientSignal/Code/Include/GradientSignal/ImageAsset.h index 500251d924..14920a2370 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/ImageAsset.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/ImageAsset.h @@ -62,6 +62,4 @@ namespace GradientSignal } }; - float GetValueFromImageAsset(AZStd::span imageData, const AZ::RHI::ImageDescriptor& imageDescriptor, const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue); - } // namespace GradientSignal diff --git a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp index fe4c134ed2..ddbba771d7 100644 --- a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -226,6 +227,60 @@ namespace GradientSignal m_imageData = m_configuration.m_imageAsset->GetSubImageData(0, 0); } + float ImageGradientComponent::GetValueFromImageData(const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue) const + { + if (!m_imageData.empty()) + { + const AZ::RHI::ImageDescriptor& imageDescriptor = m_configuration.m_imageAsset->GetImageDescriptor(); + auto width = imageDescriptor.m_size.m_width; + auto height = imageDescriptor.m_size.m_height; + + if (width > 0 && height > 0) + { + // When "rasterizing" from uvs, a range of 0-1 has slightly different meanings depending on the sampler state. + // For repeating states (Unbounded/None, Repeat), a uv value of 1 should wrap around back to our 0th pixel. + // For clamping states (Clamp to Zero, Clamp to Edge), a uv value of 1 should point to the last pixel. + + // We assume here that the code handling sampler states has handled this for us in the clamping cases + // by reducing our uv by a small delta value such that anything that wants the last pixel has a value + // just slightly less than 1. + + // Keeping that in mind, we scale our uv from 0-1 to 0-image size inclusive. So a 4-pixel image will scale + // uv values of 0-1 to 0-4, not 0-3 as you might expect. This is because we want the following range mappings: + // [0 - 1/4) = pixel 0 + // [1/4 - 1/2) = pixel 1 + // [1/2 - 3/4) = pixel 2 + // [3/4 - 1) = pixel 3 + // [1 - 1 1/4) = pixel 0 + // ... + + // Also, based on our tiling settings, we extend the size of our image virtually by a factor of tilingX and tilingY. + // A 16x16 pixel image and tilingX = tilingY = 1 maps the uv range of 0-1 to 0-16 pixels. + // A 16x16 pixel image and tilingX = tilingY = 1.5 maps the uv range of 0-1 to 0-24 pixels. + + const AZ::Vector3 tiledDimensions((width * tilingX), + (height * tilingY), + 0.0f); + + // Convert from uv space back to pixel space + AZ::Vector3 pixelLookup = (uvw * tiledDimensions); + + // UVs outside the 0-1 range are treated as infinitely tiling, so that we behave the same as the + // other gradient generators. As mentioned above, if clamping is desired, we expect it to be applied + // outside of this function. + auto x = aznumeric_cast(pixelLookup.GetX()) % width; + auto y = aznumeric_cast(pixelLookup.GetY()) % height; + + // Flip the y because images are stored in reverse of our world axes + y = (height - 1) - y; + + return AZ::RPI::GetImageDataPixelValue(m_imageData, imageDescriptor, x, y); + } + } + + return defaultValue; + } + void ImageGradientComponent::Activate() { // This will immediately call OnGradientTransformChanged and initialize m_gradientTransform. @@ -325,8 +380,8 @@ namespace GradientSignal if (!wasPointRejected) { - return GetValueFromImageAsset( - m_imageData, m_configuration.m_imageAsset->GetImageDescriptor(), uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f); + return GetValueFromImageData( + uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f); } } @@ -358,8 +413,8 @@ namespace GradientSignal if (!wasPointRejected) { - outValues[index] = GetValueFromImageAsset( - m_imageData, m_configuration.m_imageAsset->GetImageDescriptor(), uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f); + outValues[index] = GetValueFromImageData( + uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f); } else { diff --git a/Gems/GradientSignal/Code/Source/Components/SurfaceAltitudeGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/SurfaceAltitudeGradientComponent.cpp index b79606818b..0206833a63 100644 --- a/Gems/GradientSignal/Code/Source/Components/SurfaceAltitudeGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/SurfaceAltitudeGradientComponent.cpp @@ -202,13 +202,9 @@ namespace GradientSignal float SurfaceAltitudeGradientComponent::GetValue(const GradientSampleParams& sampleParams) const { - AZStd::shared_lock lock(m_cacheMutex); - - SurfaceData::SurfacePointList points; - SurfaceData::SurfaceDataSystemRequestBus::Broadcast(&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePoints, - sampleParams.m_position, m_configuration.m_surfaceTagsToSample, points); - - return CalculateAltitudeRatio(points, m_configuration.m_altitudeMin, m_configuration.m_altitudeMax); + float result = 0.0f; + GetValues(AZStd::span(&sampleParams.m_position, 1), AZStd::span(&result, 1)); + return result; } void SurfaceAltitudeGradientComponent::GetValues(AZStd::span positions, AZStd::span outValues) const @@ -220,30 +216,27 @@ namespace GradientSignal } AZStd::shared_lock lock(m_cacheMutex); - bool valuesFound = false; - // Rather than calling GetSurfacePoints on the EBus repeatedly in a loop, we instead pass a lambda into the EBus that contains - // the loop within it so that we can avoid the repeated EBus-calling overhead. + SurfaceData::SurfacePointList points; SurfaceData::SurfaceDataSystemRequestBus::Broadcast( - [this, positions, &outValues, &valuesFound](SurfaceData::SurfaceDataSystemRequestBus::Events* surfaceDataRequests) - { - // It's possible that there's nothing connected to the EBus, so keep track of the fact that we have valid results. - valuesFound = true; - SurfaceData::SurfacePointList points; - - // For each position, call GetSurfacePoints() and turn the height into a 0-1 value based on our min/max altitudes. - for (size_t index = 0; index < positions.size(); index++) - { - points.Clear(); - surfaceDataRequests->GetSurfacePoints(positions[index], m_configuration.m_surfaceTagsToSample, points); - outValues[index] = CalculateAltitudeRatio(points, m_configuration.m_altitudeMin, m_configuration.m_altitudeMax); - } - }); - - if (!valuesFound) + &SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromList, positions, m_configuration.m_surfaceTagsToSample, + points); + + // For each position, turn the height into a 0-1 value based on our min/max altitudes. + for (size_t index = 0; index < positions.size(); index++) { - // No surface data, so no output values. - AZStd::fill(outValues.begin(), outValues.end(), 0.0f); + if (!points.IsEmpty(index)) + { + // Get the point with the highest Z value and use that for the altitude. + const float highestAltitude = points.GetHighestSurfacePoint(index).m_position.GetZ(); + + // Turn the absolute altitude value into a 0-1 value by returning the % of the given altitude range that it falls at. + outValues[index] = GetRatio(m_configuration.m_altitudeMin, m_configuration.m_altitudeMax, highestAltitude); + } + else + { + outValues[index] = 0.0f; + } } } diff --git a/Gems/GradientSignal/Code/Source/Components/SurfaceMaskGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/SurfaceMaskGradientComponent.cpp index 56bb846ca4..1b8fd36148 100644 --- a/Gems/GradientSignal/Code/Source/Components/SurfaceMaskGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/SurfaceMaskGradientComponent.cpp @@ -162,16 +162,7 @@ namespace GradientSignal float SurfaceMaskGradientComponent::GetValue(const GradientSampleParams& params) const { float result = 0.0f; - - if (!m_configuration.m_surfaceTagList.empty()) - { - SurfaceData::SurfacePointList points; - SurfaceData::SurfaceDataSystemRequestBus::Broadcast(&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePoints, - params.m_position, m_configuration.m_surfaceTagList, points); - - result = GetMaxSurfaceWeight(points); - } - + GetValues(AZStd::span(¶ms.m_position, 1), AZStd::span(&result, 1)); return result; } @@ -183,34 +174,31 @@ namespace GradientSignal return; } - bool valuesFound = false; + // Initialize all our output values to 0. + AZStd::fill(outValues.begin(), outValues.end(), 0.0f); if (!m_configuration.m_surfaceTagList.empty()) { - // Rather than calling GetSurfacePoints on the EBus repeatedly in a loop, we instead pass a lambda into the EBus that contains - // the loop within it so that we can avoid the repeated EBus-calling overhead. + SurfaceData::SurfacePointList points; SurfaceData::SurfaceDataSystemRequestBus::Broadcast( - [this, positions, &outValues, &valuesFound](SurfaceData::SurfaceDataSystemRequestBus::Events* surfaceDataRequests) + &SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromList, positions, m_configuration.m_surfaceTagList, + points); + + // For each position, get the max surface weight that matches our filter and that appears at that position. + points.EnumeratePoints( + [&outValues]( + size_t inPositionIndex, [[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& normal, + const SurfaceData::SurfaceTagWeights& masks) -> bool { - // It's possible that there's nothing connected to the EBus, so keep track of the fact that we have valid results. - valuesFound = true; - SurfaceData::SurfacePointList points; - - for (size_t index = 0; index < positions.size(); index++) - { - points.Clear(); - surfaceDataRequests->GetSurfacePoints(positions[index], m_configuration.m_surfaceTagList, points); - outValues[index] = GetMaxSurfaceWeight(points); - } + masks.EnumerateWeights( + [inPositionIndex, &outValues]([[maybe_unused]] AZ::Crc32 surfaceType, float weight) -> bool + { + outValues[inPositionIndex] = AZ::GetMax(AZ::GetClamp(weight, 0.0f, 1.0f), outValues[inPositionIndex]); + return true; + }); + return true; }); } - - if (!valuesFound) - { - // No surface tags, so no output values. - AZStd::fill(outValues.begin(), outValues.end(), 0.0f); - } - } size_t SurfaceMaskGradientComponent::GetNumTags() const diff --git a/Gems/GradientSignal/Code/Source/Components/SurfaceSlopeGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/SurfaceSlopeGradientComponent.cpp index fdfa9dd5f8..09f604c919 100644 --- a/Gems/GradientSignal/Code/Source/Components/SurfaceSlopeGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/SurfaceSlopeGradientComponent.cpp @@ -205,14 +205,9 @@ namespace GradientSignal float SurfaceSlopeGradientComponent::GetValue(const GradientSampleParams& sampleParams) const { - SurfaceData::SurfacePointList points; - SurfaceData::SurfaceDataSystemRequestBus::Broadcast(&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePoints, - sampleParams.m_position, m_configuration.m_surfaceTagsToSample, points); - - const float angleMin = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMin, 0.0f, 90.0f)); - const float angleMax = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMax, 0.0f, 90.0f)); - - return GetSlopeRatio(points, angleMin, angleMax); + float result = 0.0f; + GetValues(AZStd::span(&sampleParams.m_position, 1), AZStd::span(&result, 1)); + return result; } void SurfaceSlopeGradientComponent::GetValues(AZStd::span positions, AZStd::span outValues) const @@ -223,32 +218,49 @@ namespace GradientSignal return; } - bool valuesFound = false; - - // Rather than calling GetSurfacePoints on the EBus repeatedly in a loop, we instead pass a lambda into the EBus that contains - // the loop within it so that we can avoid the repeated EBus-calling overhead. + SurfaceData::SurfacePointList points; SurfaceData::SurfaceDataSystemRequestBus::Broadcast( - [this, positions, &outValues, &valuesFound](SurfaceData::SurfaceDataSystemRequestBus::Events* surfaceDataRequests) - { - // It's possible that there's nothing connected to the EBus, so keep track of the fact that we have valid results. - valuesFound = true; - SurfaceData::SurfacePointList points; + &SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromList, positions, m_configuration.m_surfaceTagsToSample, + points); - const float angleMin = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMin, 0.0f, 90.0f)); - const float angleMax = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMax, 0.0f, 90.0f)); + const float angleMin = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMin, 0.0f, 90.0f)); + const float angleMax = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMax, 0.0f, 90.0f)); - for (size_t index = 0; index < positions.size(); index++) + for (size_t index = 0; index < positions.size(); index++) + { + if (points.IsEmpty(index)) + { + outValues[index] = 0.0f; + } + else + { + // Assuming our surface normal vector is actually normalized, we can get the slope + // by just grabbing the Z value. It's the same thing as normal.Dot(AZ::Vector3::CreateAxisZ()). + auto highestSurfacePoint = points.GetHighestSurfacePoint(index); + AZ_Assert( + highestSurfacePoint.m_normal.GetNormalized().IsClose(highestSurfacePoint.m_normal), + "Surface normals are expected to be normalized"); + const float slope = highestSurfacePoint.m_normal.GetZ(); + // Convert slope back to an angle so that we can lerp in "angular space", not "slope value space". + // (We want our 0-1 range to be linear across the range of angles) + const float slopeAngle = acosf(slope); + + switch (m_configuration.m_rampType) { - points.Clear(); - surfaceDataRequests->GetSurfacePoints(positions[index], m_configuration.m_surfaceTagsToSample, points); - outValues[index] = GetSlopeRatio(points, angleMin, angleMax); + case SurfaceSlopeGradientConfig::RampType::SMOOTH_STEP: + outValues[index] = m_configuration.m_smoothStep.GetSmoothedValue(GetRatio(angleMin, angleMax, slopeAngle)); + break; + case SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_UP: + // For ramp up, linearly interpolate from min to max. + outValues[index] = GetRatio(angleMin, angleMax, slopeAngle); + break; + case SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_DOWN: + default: + // For ramp down, linearly interpolate from max to min. + outValues[index] = GetRatio(angleMax, angleMin, slopeAngle); + break; } - }); - - if (!valuesFound) - { - // No surface tags, so no output values. - AZStd::fill(outValues.begin(), outValues.end(), 0.0f); + } } } diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorConstantGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorConstantGradientComponent.h index 87228671b3..995160547f 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorConstantGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorConstantGradientComponent.h @@ -26,6 +26,6 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Returns a specified value as a gradient when sampled"; static constexpr const char* const s_icon = "Editor/Icons/Components/Gradient.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/Gradient.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = ""; }; } diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorDitherGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorDitherGradientComponent.h index b01968eed6..00b8d737b7 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorDitherGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorDitherGradientComponent.h @@ -26,7 +26,7 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Applies ordered dithering to the input gradient"; static constexpr const char* const s_icon = "Editor/Icons/Components/GradientModifier.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/GradientModifier.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/reference/gradient-modifiers/dither-gradient-modifier/"; protected: AZ::u32 ConfigurationChanged() override; diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorGradientSurfaceDataComponent.cpp b/Gems/GradientSignal/Code/Source/Editor/EditorGradientSurfaceDataComponent.cpp index 692576b3b3..201b658436 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorGradientSurfaceDataComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Editor/EditorGradientSurfaceDataComponent.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace GradientSignal { @@ -107,7 +108,8 @@ namespace GradientSignal // Create a fake surface point with the position we're sampling. AzFramework::SurfaceData::SurfacePoint point; point.m_position = params.m_position; - SurfaceData::SurfacePointList pointList = { { point } }; + point.m_normal = AZ::Vector3::CreateAxisZ(); + SurfaceData::SurfacePointList pointList = AZStd::span(&point, 1); // Send it into the component, see what emerges m_component.ModifySurfacePoints(pointList); @@ -117,8 +119,8 @@ namespace GradientSignal // the underlying logic ever changes to allow separate ranges per tag. float result = 0.0f; pointList.EnumeratePoints([&result]( - [[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& normal, - const SurfaceData::SurfaceTagWeights& masks) -> bool + [[maybe_unused]] size_t inPositionIndex, [[maybe_unused]] const AZ::Vector3& position, + [[maybe_unused]] const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool { masks.EnumerateWeights( [&result]([[maybe_unused]] AZ::Crc32 surfaceType, float weight) -> bool diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorGradientSurfaceDataComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorGradientSurfaceDataComponent.h index e79571e662..7d2a7a5776 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorGradientSurfaceDataComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorGradientSurfaceDataComponent.h @@ -33,7 +33,7 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Enables a gradient to emit surface tags"; static constexpr const char* const s_icon = "Editor/Icons/Components/SurfaceData.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/SurfaceData.png"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/reference/"; private: AZ::u32 ConfigurationChanged() override; diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorGradientTransformComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorGradientTransformComponent.h index d2e2dc38eb..b1a15c0732 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorGradientTransformComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorGradientTransformComponent.h @@ -35,7 +35,7 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Transforms coordinates into a space relative to a shape, allowing other transform and sampling modifications"; static constexpr const char* const s_icon = "Editor/Icons/Components/GradientModifier.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/GradientModifier.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/reference/gradient-modifiers/gradient-transform-modifier/"; private: AZ::u32 ConfigurationChanged() override; diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorImageBuilderComponent.cpp b/Gems/GradientSignal/Code/Source/Editor/EditorImageBuilderComponent.cpp deleted file mode 100644 index 062f34b644..0000000000 --- a/Gems/GradientSignal/Code/Source/Editor/EditorImageBuilderComponent.cpp +++ /dev/null @@ -1,307 +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 "EditorImageBuilderComponent.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace GradientSignal -{ - EditorImageBuilderPluginComponent::EditorImageBuilderPluginComponent() - { - // AZ Components should only initialize their members to null and empty in constructor - // after construction, they may be deserialized from file. - } - - EditorImageBuilderPluginComponent::~EditorImageBuilderPluginComponent() - { - } - - void EditorImageBuilderPluginComponent::Init() - { - } - - void EditorImageBuilderPluginComponent::Activate() - { - // Activate is where you'd perform registration with other objects and systems. - // Since we want to register our builder, we do that here: - AssetBuilderSDK::AssetBuilderDesc builderDescriptor; - builderDescriptor.m_name = "Gradient Image Builder"; - builderDescriptor.m_version = 1; - - builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.tif", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); - builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.tiff", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); - builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.png", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); - builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.bmp", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); - builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.jpg", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); - builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.jpeg", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); - builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.tga", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); - builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.gif", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); - builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.bt", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); - - builderDescriptor.m_busId = EditorImageBuilderWorker::GetUUID(); - builderDescriptor.m_createJobFunction = AZStd::bind(&EditorImageBuilderWorker::CreateJobs, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); - builderDescriptor.m_processJobFunction = AZStd::bind(&EditorImageBuilderWorker::ProcessJob, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); - m_imageBuilder.BusConnect(builderDescriptor.m_busId); - - EBUS_EVENT(AssetBuilderSDK::AssetBuilderBus, RegisterBuilderInformation, builderDescriptor); - } - - void EditorImageBuilderPluginComponent::Deactivate() - { - m_imageBuilder.BusDisconnect(); - } - - void EditorImageBuilderPluginComponent::Reflect(AZ::ReflectContext* context) - { - ImageSettings::Reflect(context); - - AZ::SerializeContext* serializeContext = azrtti_cast(context); - if (serializeContext) - { - serializeContext->Class()->Version(0) - ->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector({ AssetBuilderSDK::ComponentTags::AssetBuilder })); - } - } - - EditorImageBuilderWorker::EditorImageBuilderWorker() - { - } - - EditorImageBuilderWorker::~EditorImageBuilderWorker() - { - } - - void EditorImageBuilderWorker::ShutDown() - { - // it is important to note that this will be called on a different thread than your process job thread - m_isShuttingDown = true; - } - - // this happens early on in the file scanning pass - // this function should consistently always create the same jobs, and should do no checking whether the job is up to date or not - just be consistent. - void EditorImageBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) - { - AZStd::string fullPath; - AzFramework::StringFunc::Path::Join(request.m_watchFolder.data(), request.m_sourceFile.data(), fullPath, true, true); - - // Check file for _GSI suffix/pattern which assumes processing will occur whether or not settings are provided - AZStd::string patternPath = fullPath; - AZStd::to_upper(patternPath.begin(), patternPath.end()); - bool patternMatched = patternPath.rfind("_GSI.") != AZStd::string::npos; - - // Determine if a settings file has been provided - AZStd::string settingsPath = fullPath + "." + s_gradientImageSettingsExtension; - bool settingsExist = AZ::IO::SystemFile::Exists(settingsPath.data()); - - // If the settings file is modified the image must be reprocessed - AssetBuilderSDK::SourceFileDependency sourceFileDependency; - sourceFileDependency.m_sourceFileDependencyPath = settingsPath; - response.m_sourceFileDependencyList.push_back(sourceFileDependency); - - // If no settings file was provided then skip the file, unless the file name matches the convenience pattern - if (!patternMatched && !settingsExist) - { - //do nothing if settings aren't provided - response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; - return; - } - - AZStd::unique_ptr settingsPtr; - if (settingsExist) - { - settingsPtr.reset(AZ::Utils::LoadObjectFromFile(settingsPath)); - } - - // If the settings file didn't load then skip the file, unless the file name matches the convenience pattern - if (!patternMatched && !settingsPtr) - { - AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Failed to create gradient image conversion job for %s.\nFailed loading settings %s.\n", fullPath.data(), settingsPath.data()); - response.m_result = AssetBuilderSDK::CreateJobsResultCode::Failed; - return; - } - - // If settings loaded but processing is disabled then skip the file - if (settingsPtr && !settingsPtr->m_shouldProcess) - { - //do nothing if settings disable processing - response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; - return; - } - - // Get the extension of the file - AZStd::string ext; - AzFramework::StringFunc::Path::GetExtension(request.m_sourceFile.data(), ext, false); - AZStd::to_upper(ext.begin(), ext.end()); - - // We process the same file for all platforms - for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms) - { - AssetBuilderSDK::JobDescriptor descriptor; - descriptor.m_jobKey = ext + " Compile (Gradient Image)"; - descriptor.SetPlatformIdentifier(info.m_identifier.data()); - descriptor.m_critical = false; - response.m_createJobOutputs.push_back(descriptor); - } - - response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; - return; - } - - // later on, this function will be called for jobs that actually need doing. - // the request will contain the CreateJobResponse you constructed earlier, including any keys and values you placed into the hash table - void EditorImageBuilderWorker::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) - { - // Before we begin, let's make sure we are not meant to abort. - AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId); - if (jobCancelListener.IsCancelled()) - { - AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Cancelled gradient image conversion job for %s.\nCancellation requested.\n", request.m_fullPath.data()); - response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled; - return; - } - - if (m_isShuttingDown) - { - AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Cancelled gradient image conversion job for %s.\nShutdown requested.\n", request.m_fullPath.data()); - response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled; - return; - } - - // Do conversion and get exported file's path - AZ_TracePrintf(AssetBuilderSDK::InfoWindow, "Performing gradient image conversion job for %s\n", request.m_fullPath.data()); - - auto imageAsset = LoadImageFromPath(request.m_fullPath); - - if (!imageAsset) - { - AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Failed gradient image conversion job for %s.\nFailed loading source image %s.\n", request.m_fullPath.data(), request.m_fullPath.data()); - return; - } - - auto imageSettings = LoadImageSettingsFromPath(request.m_fullPath); - - if (!imageSettings) - { - AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Failed gradient image conversion job for %s.\nFailed loading source image %s.\n", request.m_fullPath.data(), request.m_fullPath.data()); - return; - } - - // ChannelMask is 8 bits, and the bits are assumed to be as follows: 0b0000ABGR - imageAsset = ConvertImage(*imageAsset, *imageSettings); - - //generate export file name - QDir dir(request.m_tempDirPath.data()); - if (!dir.exists()) - { - dir.mkpath("."); - } - - AZStd::string fileName; - AZStd::string outputPath; - AzFramework::StringFunc::Path::GetFileName(request.m_fullPath.data(), fileName); - fileName += AZStd::string(".") + s_gradientImageExtension; - AzFramework::StringFunc::Path::Join(request.m_tempDirPath.data(), fileName.data(), outputPath, true, true); - AZ_TracePrintf(AssetBuilderSDK::InfoWindow, "Output path for gradient image conversion: %s\n", outputPath.data()); - - //save asset - if (!AZ::Utils::SaveObjectToFile(outputPath, AZ::DataStream::ST_XML, imageAsset.get())) - { - AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Failed gradient image conversion job for %s.\nFailed saving output file %s.\n", request.m_fullPath.data(), outputPath.data()); - return; - } - - // Report the image-import result - AssetBuilderSDK::JobProduct jobProduct; - if(!AssetBuilderSDK::OutputObject(imageAsset.get(), outputPath, azrtti_typeid(), 2, jobProduct)) - { - AZ_Error(AssetBuilderSDK::ErrorWindow, false, "Failed to output product dependencies."); - return; - } - - response.m_outputProducts.push_back(jobProduct); - response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; - AZ_TracePrintf(AssetBuilderSDK::InfoWindow, "Completed gradient image conversion job for %s.\nSucceeded saving output file %s.\n", request.m_fullPath.data(), outputPath.data()); - } - - AZ::Uuid EditorImageBuilderWorker::GetUUID() - { - return AZ::Uuid::CreateString("{7520DF20-16CA-4CF6-A6DB-D96759A09EE4}"); - } - - static AZStd::unique_ptr AtomLoadImageFromPath(const AZStd::string& fullPath) - { - ImageProcessingAtom::IImageObjectPtr imageObject; - ImageProcessingAtom::ImageProcessingRequestBus::BroadcastResult(imageObject, &ImageProcessingAtom::ImageProcessingRequests::LoadImage, - fullPath); - - if (!imageObject) - { - return {}; - } - - //create a new image asset - auto imageAsset = AZStd::make_unique(); - - if (!imageAsset) - { - return {}; - } - - imageAsset->m_imageWidth = imageObject->GetWidth(0); - imageAsset->m_imageHeight = imageObject->GetHeight(0); - imageAsset->m_imageFormat = imageObject->GetPixelFormat(); - - AZ::u8* mem = nullptr; - AZ::u32 pitch = 0; - AZ::u32 mipBufferSize = imageObject->GetMipBufSize(0); - imageObject->GetImagePointer(0, mem, pitch); - - imageAsset->m_imageData = { mem, mem + mipBufferSize }; - - return imageAsset; - } - - AZStd::unique_ptr EditorImageBuilderWorker::LoadImageFromPath(const AZStd::string& fullPath) - { - return AtomLoadImageFromPath(fullPath); - } - - AZStd::unique_ptr EditorImageBuilderWorker::LoadImageSettingsFromPath(const AZStd::string& fullPath) - { - // Determine if a settings file has been provided - AZStd::string settingsPath = fullPath + "." + s_gradientImageSettingsExtension; - bool settingsExist = AZ::IO::SystemFile::Exists(settingsPath.data()); - - if (settingsExist) - { - return AZStd::unique_ptr{AZ::Utils::LoadObjectFromFile(settingsPath)}; - } - else - { - return AZStd::make_unique(); - } - } - -} // namespace GradientSignal diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorImageBuilderComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorImageBuilderComponent.h deleted file mode 100644 index 60fa985bf7..0000000000 --- a/Gems/GradientSignal/Code/Source/Editor/EditorImageBuilderComponent.h +++ /dev/null @@ -1,70 +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 - -namespace GradientSignal -{ - class ImageAsset; - class ImageSettings; - - //! Builder to process images - class EditorImageBuilderWorker - : public AssetBuilderSDK::AssetBuilderCommandBus::Handler - { - public: - AZ_CLASS_ALLOCATOR(EditorImageBuilderWorker, AZ::SystemAllocator, 0); - - EditorImageBuilderWorker(); - ~EditorImageBuilderWorker(); - - //! Asset Builder Callback Functions - void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response); - void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response); - - ////////////////////////////////////////////////////////////////////////// - //!AssetBuilderSDK::AssetBuilderCommandBus interface - void ShutDown() override; // if you get this you must fail all existing jobs and return. - ////////////////////////////////////////////////////////////////////////// - - static AZ::Uuid GetUUID(); - static AZStd::unique_ptr LoadImageFromPath(const AZStd::string& fullPath); - static AZStd::unique_ptr LoadImageSettingsFromPath(const AZStd::string& fullPath); - - private: - bool m_isShuttingDown = false; - }; - - //! EditorImageBuilderPluginComponent is to handle the lifecycle of ImageBuilder module. - class EditorImageBuilderPluginComponent - : public AZ::Component - { - public: - AZ_COMPONENT(EditorImageBuilderPluginComponent, "{BF60FBB2-E124-4CB9-91CD-E6E640424C99}"); - static void Reflect(AZ::ReflectContext* context); - - EditorImageBuilderPluginComponent(); // avoid initialization here. - - ////////////////////////////////////////////////////////////////////////// - // AZ::Component - virtual void Init(); // create objects, allocate memory and initialize yourself without reaching out to the outside world - virtual void Activate(); // reach out to the outside world and connect up to what you need to, register things, etc. - virtual void Deactivate(); // unregister things, disconnect from the outside world - ////////////////////////////////////////////////////////////////////////// - - virtual ~EditorImageBuilderPluginComponent(); // free memory an uninitialize yourself. - - private: - EditorImageBuilderWorker m_imageBuilder; - }; - -}// namespace GradientSignal diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorImageGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorImageGradientComponent.h index f84766e73b..3eeff47d6b 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorImageGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorImageGradientComponent.h @@ -26,6 +26,6 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Generates a gradient by sampling an image asset"; static constexpr const char* const s_icon = "Editor/Icons/Components/Gradient.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/Gradient.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = ""; }; } diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorImageProcessingSystemComponent.cpp b/Gems/GradientSignal/Code/Source/Editor/EditorImageProcessingSystemComponent.cpp deleted file mode 100644 index e665d46468..0000000000 --- a/Gems/GradientSignal/Code/Source/Editor/EditorImageProcessingSystemComponent.cpp +++ /dev/null @@ -1,147 +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 "EditorImageProcessingSystemComponent.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -// warning C4800: 'uint': forcing value to bool 'true' or 'false' (performance warning) -// warning C4251: 'QBrush::d': class 'QScopedPointer' needs to have dll-interface to be used by clients of class 'QBrush' -AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") -#include -AZ_POP_DISABLE_WARNING -#include -#include - -namespace GradientSignal -{ - void EditorImageProcessingSystemComponent::Reflect(AZ::ReflectContext* context) - { - if (auto serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(0) - ; - - if (auto editContext = serializeContext->GetEditContext()) - { - editContext->Class("EditorImageProcessingSystemComponent", "Handles adding gradient image setting context menus") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b)) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ; - } - } - } - - void EditorImageProcessingSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) - { - services.push_back(AZ_CRC("GradientImageBuilderService", 0x00cea88a)); - } - - void EditorImageProcessingSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) - { - services.push_back(AZ_CRC("GradientImageBuilderService", 0x00cea88a)); - } - - void EditorImageProcessingSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) - { - (void)services; - } - - void EditorImageProcessingSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services) - { - (void)services; - } - - void EditorImageProcessingSystemComponent::Init() - { - - } - - void EditorImageProcessingSystemComponent::Activate() - { - AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusConnect(); - } - - void EditorImageProcessingSystemComponent::Deactivate() - { - AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusDisconnect(); - } - - void EditorImageProcessingSystemComponent::AddContextMenuActions(QWidget* /*caller*/, QMenu* menu, const AZStd::vector& entries) - { - // Register right click menu - using namespace AzToolsFramework::AssetBrowser; - auto entryIt = AZStd::find_if(entries.begin(), entries.end(), [](const AssetBrowserEntry* entry) - { - return entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source; - }); - - if (entryIt == entries.end()) - { - return; - } - - auto source = azrtti_cast(*entryIt); - if (!HandlesSource(source)) - { - return; - } - - AZStd::string settingsPath = source->GetFullPath() + "." + s_gradientImageSettingsExtension; - bool settingsExist = AZ::IO::SystemFile::Exists(settingsPath.data()); - - if(settingsExist) - { - menu->addAction("Edit Gradient Image Settings...", [settingsPath]() - { - bool result = false; - AZ::Data::AssetInfo assetInfo; - AZStd::string rootPath; - AzToolsFramework::AssetSystemRequestBus::BroadcastResult(result, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, settingsPath.data(), assetInfo, rootPath); - if (result) - { - AZ::Data::Asset asset = AZ::Data::AssetManager::Instance().FindOrCreateAsset(assetInfo.m_assetId, AZ::Data::AssetLoadBehavior::Default); - AzToolsFramework::AssetEditor::AssetEditorRequestsBus::Broadcast(&AzToolsFramework::AssetEditor::AssetEditorRequests::OpenAssetEditor, asset); - } - }); - } - else - { - menu->addAction("Enable Gradient Image Settings", [settingsPath]() - { - GradientSignal::ImageSettings imageSettings; - AZ::Utils::SaveObjectToFile(settingsPath, AZ::DataStream::ST_XML, &imageSettings); - }); - } - } - - bool EditorImageProcessingSystemComponent::HandlesSource(const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry) const - { - AZStd::string targetExtension = entry->GetExtension(); - - return AZStd::wildcard_match("*.tif", targetExtension.data()) - || AZStd::wildcard_match("*.tiff", targetExtension.data()) - || AZStd::wildcard_match("*.png", targetExtension.data()) - || AZStd::wildcard_match("*.bmp", targetExtension.data()) - || AZStd::wildcard_match("*.jpg", targetExtension.data()) - || AZStd::wildcard_match("*.jpeg", targetExtension.data()) - || AZStd::wildcard_match("*.tga", targetExtension.data()) - || AZStd::wildcard_match("*.gif", targetExtension.data()); - } -} // namespace GradientSignal diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorImageProcessingSystemComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorImageProcessingSystemComponent.h deleted file mode 100644 index e8ec38a933..0000000000 --- a/Gems/GradientSignal/Code/Source/Editor/EditorImageProcessingSystemComponent.h +++ /dev/null @@ -1,48 +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 - -namespace GradientSignal -{ - class EditorImageProcessingSystemComponent - : public AZ::Component - , protected AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler - { - public: - AZ_COMPONENT(EditorImageProcessingSystemComponent, "{3AF5AB01-161C-4762-A73F-BBDD2B878F6A}"); - - static void Reflect(AZ::ReflectContext* context); - - static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services); - static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services); - static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services); - static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services); - - protected: - - //////////////////////////////////////////////////////////////////////// - // AZ::Component interface implementation - void Init() override; - void Activate() override; - void Deactivate() override; - //////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////// - // AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler - void AddContextMenuActions(QWidget* /*caller*/, QMenu* menu, const AZStd::vector& entries) override; - //////////////////////////////////////////////////////////////////////// - - private: - bool HandlesSource(const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry) const; - }; -} // namespace GradientSignal diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorInvertGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorInvertGradientComponent.h index 652ddc3279..077963cd82 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorInvertGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorInvertGradientComponent.h @@ -26,6 +26,6 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Inverts a gradient's values"; static constexpr const char* const s_icon = "Editor/Icons/Components/GradientModifier.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/GradientModifier.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/reference/gradient-modifiers/invert-gradient-modifier/"; }; } diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorLevelsGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorLevelsGradientComponent.h index b43f9bbb73..ffad2bc2cd 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorLevelsGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorLevelsGradientComponent.h @@ -26,6 +26,6 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Modifies an input gradient's signal using low/mid/high points and allows clamping of min/max output values"; static constexpr const char* const s_icon = "Editor/Icons/Components/GradientModifier.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/GradientModifier.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/reference/gradient-modifiers/levels-gradient-modifier/"; }; } diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorMixedGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorMixedGradientComponent.h index c2ca707014..236bc5ee30 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorMixedGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorMixedGradientComponent.h @@ -58,7 +58,7 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Generates a new gradient by combining other gradients"; static constexpr const char* const s_icon = "Editor/Icons/Components/GradientModifier.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/GradientModifier.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/reference/gradient-modifiers/gradient-mixer/"; protected: diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorPerlinGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorPerlinGradientComponent.h index 1c39cf8faa..35cefe1344 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorPerlinGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorPerlinGradientComponent.h @@ -27,7 +27,7 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Generates a gradient by sampling a perlin noise generator"; static constexpr const char* const s_icon = "Editor/Icons/Components/Gradient.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/Gradient.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = ""; private: AZ::Crc32 OnGenerateRandomSeed(); diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorPosterizeGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorPosterizeGradientComponent.h index 0078e2865f..d1f13ad0fe 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorPosterizeGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorPosterizeGradientComponent.h @@ -26,6 +26,6 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Divides an input gradient's signal into a specified number of bands"; static constexpr const char* const s_icon = "Editor/Icons/Components/GradientModifier.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/GradientModifier.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/reference/gradient-modifiers/posterize-gradient-modifier/"; }; } diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorRandomGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorRandomGradientComponent.h index f5f9daee0a..3ac13bc7c7 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorRandomGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorRandomGradientComponent.h @@ -29,7 +29,7 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Generates a gradient by sampling a random noise generator"; static constexpr const char* const s_icon = "Editor/Icons/Components/Gradient.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/Gradient.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = ""; private: AZ::Crc32 OnGenerateRandomSeed(); diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorReferenceGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorReferenceGradientComponent.h index bf29b270bd..b7fb6a5b67 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorReferenceGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorReferenceGradientComponent.h @@ -26,6 +26,6 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "References another gradient"; static constexpr const char* const s_icon = "Editor/Icons/Components/Gradient.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/Gradient.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = ""; }; } diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorShapeAreaFalloffGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorShapeAreaFalloffGradientComponent.h index 7772dfe7c5..1d36c1b76f 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorShapeAreaFalloffGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorShapeAreaFalloffGradientComponent.h @@ -26,6 +26,6 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Generates a gradient based on distance from a shape"; static constexpr const char* const s_icon = "Editor/Icons/Components/Gradient.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/Gradient.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = ""; }; } diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorSmoothStepGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorSmoothStepGradientComponent.h index 4a43920a6c..90db38139c 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorSmoothStepGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorSmoothStepGradientComponent.h @@ -26,6 +26,6 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Generates a gradient with fall off, which creates a smoother input gradient"; static constexpr const char* const s_icon = "Editor/Icons/Components/GradientModifier.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/GradientModifier.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/reference/gradient-modifiers/smooth-step-gradient-modifier/"; }; } diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorSurfaceAltitudeGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorSurfaceAltitudeGradientComponent.h index f40176a9f5..879684e7a8 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorSurfaceAltitudeGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorSurfaceAltitudeGradientComponent.h @@ -26,7 +26,7 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Generates a gradient based on height within a range"; static constexpr const char* const s_icon = "Editor/Icons/Components/Gradient.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/Gradient.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = ""; // AZ::Component interface void Activate() override; diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorSurfaceMaskGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorSurfaceMaskGradientComponent.h index 9e71526541..e68d3bce32 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorSurfaceMaskGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorSurfaceMaskGradientComponent.h @@ -26,6 +26,6 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Generates a gradient based on underlying surface types"; static constexpr const char* const s_icon = "Editor/Icons/Components/Gradient.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/Gradient.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = ""; }; } diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorSurfaceSlopeGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorSurfaceSlopeGradientComponent.h index 20daa662a9..c610203428 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorSurfaceSlopeGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorSurfaceSlopeGradientComponent.h @@ -26,6 +26,6 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Generates a gradient based on surface angle"; static constexpr const char* const s_icon = "Editor/Icons/Components/Gradient.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/Gradient.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = ""; }; } diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorThresholdGradientComponent.h b/Gems/GradientSignal/Code/Source/Editor/EditorThresholdGradientComponent.h index cbeb626c5e..94ba4194ac 100644 --- a/Gems/GradientSignal/Code/Source/Editor/EditorThresholdGradientComponent.h +++ b/Gems/GradientSignal/Code/Source/Editor/EditorThresholdGradientComponent.h @@ -26,6 +26,6 @@ namespace GradientSignal static constexpr const char* const s_componentDescription = "Converts input gradient to be 0 if below the threshold or 1 if above the threshold"; static constexpr const char* const s_icon = "Editor/Icons/Components/GradientModifier.svg"; static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/GradientModifier.svg"; - static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/"; + static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/reference/gradient-modifiers/threshold-gradient-modifier/"; }; } diff --git a/Gems/GradientSignal/Code/Source/GradientSignalEditorModule.cpp b/Gems/GradientSignal/Code/Source/GradientSignalEditorModule.cpp index a8b578737e..098261ba7f 100644 --- a/Gems/GradientSignal/Code/Source/GradientSignalEditorModule.cpp +++ b/Gems/GradientSignal/Code/Source/GradientSignalEditorModule.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -29,7 +28,6 @@ #include #include #include -#include namespace GradientSignal { @@ -37,13 +35,11 @@ namespace GradientSignal { m_descriptors.insert(m_descriptors.end(), { GradientSignalEditorSystemComponent::CreateDescriptor(), - EditorImageProcessingSystemComponent::CreateDescriptor(), EditorSurfaceAltitudeGradientComponent::CreateDescriptor(), EditorSmoothStepGradientComponent::CreateDescriptor(), EditorSurfaceSlopeGradientComponent::CreateDescriptor(), EditorMixedGradientComponent::CreateDescriptor(), - EditorImageBuilderPluginComponent::CreateDescriptor(), EditorImageGradientComponent::CreateDescriptor(), EditorConstantGradientComponent::CreateDescriptor(), EditorThresholdGradientComponent::CreateDescriptor(), @@ -66,7 +62,6 @@ namespace GradientSignal AZ::ComponentTypeList requiredComponents = GradientSignalModule::GetRequiredSystemComponents(); requiredComponents.push_back(azrtti_typeid()); - requiredComponents.push_back(azrtti_typeid()); return requiredComponents; } diff --git a/Gems/GradientSignal/Code/Source/ImageAsset.cpp b/Gems/GradientSignal/Code/Source/ImageAsset.cpp index 05ad13d193..a8b41df949 100644 --- a/Gems/GradientSignal/Code/Source/ImageAsset.cpp +++ b/Gems/GradientSignal/Code/Source/ImageAsset.cpp @@ -15,7 +15,6 @@ #include #include -#include #include #include @@ -77,57 +76,4 @@ namespace GradientSignal return true; } - - float GetValueFromImageAsset(AZStd::span imageData, const AZ::RHI::ImageDescriptor& imageDescriptor, const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue) - { - if (!imageData.empty()) - { - auto width = imageDescriptor.m_size.m_width; - auto height = imageDescriptor.m_size.m_height; - - if (width > 0 && height > 0) - { - // When "rasterizing" from uvs, a range of 0-1 has slightly different meanings depending on the sampler state. - // For repeating states (Unbounded/None, Repeat), a uv value of 1 should wrap around back to our 0th pixel. - // For clamping states (Clamp to Zero, Clamp to Edge), a uv value of 1 should point to the last pixel. - - // We assume here that the code handling sampler states has handled this for us in the clamping cases - // by reducing our uv by a small delta value such that anything that wants the last pixel has a value - // just slightly less than 1. - - // Keeping that in mind, we scale our uv from 0-1 to 0-image size inclusive. So a 4-pixel image will scale - // uv values of 0-1 to 0-4, not 0-3 as you might expect. This is because we want the following range mappings: - // [0 - 1/4) = pixel 0 - // [1/4 - 1/2) = pixel 1 - // [1/2 - 3/4) = pixel 2 - // [3/4 - 1) = pixel 3 - // [1 - 1 1/4) = pixel 0 - // ... - - // Also, based on our tiling settings, we extend the size of our image virtually by a factor of tilingX and tilingY. - // A 16x16 pixel image and tilingX = tilingY = 1 maps the uv range of 0-1 to 0-16 pixels. - // A 16x16 pixel image and tilingX = tilingY = 1.5 maps the uv range of 0-1 to 0-24 pixels. - - const AZ::Vector3 tiledDimensions((width * tilingX), - (height * tilingY), - 0.0f); - - // Convert from uv space back to pixel space - AZ::Vector3 pixelLookup = (uvw * tiledDimensions); - - // UVs outside the 0-1 range are treated as infinitely tiling, so that we behave the same as the - // other gradient generators. As mentioned above, if clamping is desired, we expect it to be applied - // outside of this function. - auto x = aznumeric_cast(pixelLookup.GetX()) % width; - auto y = aznumeric_cast(pixelLookup.GetY()) % height; - - // Flip the y because images are stored in reverse of our world axes - y = (height - 1) - y; - - return AZ::RPI::GetImageDataPixelValue(imageData, imageDescriptor, x, y); - } - } - - return defaultValue; - } } diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalReferencesTests.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalReferencesTests.cpp index 2e602e8c80..b0a83b670d 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalReferencesTests.cpp +++ b/Gems/GradientSignal/Code/Tests/GradientSignalReferencesTests.cpp @@ -78,8 +78,10 @@ namespace UnitTest for (int x = 0; x < dataSize; x++) { float angle = AZ::DegToRad(inputAngles[(y * dataSize) + x]); + point.m_position = AZ::Vector3(aznumeric_cast(x), aznumeric_cast(y), 0.0f); point.m_normal = AZ::Vector3(sinf(angle), 0.0f, cosf(angle)); - mockSurface->m_surfacePoints[AZStd::make_pair(static_cast(x), static_cast(y))] = { { point } }; + mockSurface->m_surfacePoints[AZStd::make_pair(static_cast(x), static_cast(y))] = + AZStd::span(&point, 1); } } ActivateEntity(surfaceEntity.get()); @@ -546,10 +548,22 @@ namespace UnitTest auto surfaceEntity = CreateEntity(); auto mockSurface = surfaceEntity->CreateComponent(); mockSurface->m_bounds = mockShapeComponentHandler.m_GetEncompassingAabb; - mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 0.0f)] = { { AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3::CreateZero() } }; - mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 0.0f)] = { { AZ::Vector3(0.0f, 0.0f, 2.0f), AZ::Vector3::CreateZero() } }; - mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 1.0f)] = { { AZ::Vector3(0.0f, 0.0f, 5.0f), AZ::Vector3::CreateZero() } }; - mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 1.0f)] = { { AZ::Vector3(0.0f, 0.0f, 10.0f), AZ::Vector3::CreateZero() } }; + AzFramework::SurfaceData::SurfacePoint mockOutputs[] = + { + { AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3::CreateAxisZ() }, + { AZ::Vector3(0.0f, 0.0f, 2.0f), AZ::Vector3::CreateAxisZ() }, + { AZ::Vector3(0.0f, 0.0f, 5.0f), AZ::Vector3::CreateAxisZ() }, + { AZ::Vector3(0.0f, 0.0f, 10.0f), AZ::Vector3::CreateAxisZ() }, + }; + + mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 0.0f)] = + AZStd::span(&mockOutputs[0], 1); + mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 0.0f)] = + AZStd::span(&mockOutputs[1], 1); + mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 1.0f)] = + AZStd::span(&mockOutputs[2], 1); + mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 1.0f)] = + AZStd::span(&mockOutputs[3], 1); ActivateEntity(surfaceEntity.get()); // We set the min/max to values other than 0-10 to help validate that they aren't used in the case of the pinned shape. @@ -583,10 +597,21 @@ namespace UnitTest auto surfaceEntity = CreateEntity(); auto mockSurface = surfaceEntity->CreateComponent(); mockSurface->m_bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(1.0f)); - mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 0.0f)] = { { AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3::CreateZero() } }; - mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 0.0f)] = { { AZ::Vector3(0.0f, 0.0f, 2.0f), AZ::Vector3::CreateZero() } }; - mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 1.0f)] = { { AZ::Vector3(0.0f, 0.0f, 5.0f), AZ::Vector3::CreateZero() } }; - mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 1.0f)] = { { AZ::Vector3(0.0f, 0.0f, 10.0f), AZ::Vector3::CreateZero() } }; + AzFramework::SurfaceData::SurfacePoint mockOutputs[] = { + { AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3::CreateAxisZ() }, + { AZ::Vector3(0.0f, 0.0f, 2.0f), AZ::Vector3::CreateAxisZ() }, + { AZ::Vector3(0.0f, 0.0f, 5.0f), AZ::Vector3::CreateAxisZ() }, + { AZ::Vector3(0.0f, 0.0f, 10.0f), AZ::Vector3::CreateAxisZ() }, + }; + + mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 0.0f)] = + AZStd::span(&mockOutputs[0], 1); + mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 0.0f)] = + AZStd::span(&mockOutputs[1], 1); + mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 1.0f)] = + AZStd::span(&mockOutputs[2], 1); + mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 1.0f)] = + AZStd::span(&mockOutputs[3], 1); ActivateEntity(surfaceEntity.get()); // We set the min/max to 0-10, but don't set a shape. @@ -642,14 +667,25 @@ namespace UnitTest auto surfaceEntity = CreateEntity(); auto mockSurface = surfaceEntity->CreateComponent(); mockSurface->m_bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(1.0f)); + AzFramework::SurfaceData::SurfacePoint mockOutputs[] = { + { AZ::Vector3(0.0f, 0.0f, -10.0f), AZ::Vector3::CreateAxisZ() }, + { AZ::Vector3(0.0f, 0.0f, -5.0f), AZ::Vector3::CreateAxisZ() }, + { AZ::Vector3(0.0f, 0.0f, 15.0f), AZ::Vector3::CreateAxisZ() }, + { AZ::Vector3(0.0f, 0.0f, 20.0f), AZ::Vector3::CreateAxisZ() }, + }; + // Altitude value below min - should result in 0.0f. - mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 0.0f)] = { { AZ::Vector3(0.0f, 0.0f, -10.0f), AZ::Vector3::CreateZero() } }; + mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 0.0f)] = + AZStd::span(&mockOutputs[0], 1); // Altitude value at exactly min - should result in 0.0f. - mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 0.0f)] = { { AZ::Vector3(0.0f, 0.0f, -5.0f), AZ::Vector3::CreateZero() } }; + mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 0.0f)] = + AZStd::span(&mockOutputs[1], 1); // Altitude value at exactly max - should result in 1.0f. - mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 1.0f)] = { { AZ::Vector3(0.0f, 0.0f, 15.0f), AZ::Vector3::CreateZero() } }; + mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 1.0f)] = + AZStd::span(&mockOutputs[2], 1); // Altitude value above max - should result in 1.0f. - mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 1.0f)] = { { AZ::Vector3(0.0f, 0.0f, 20.0f), AZ::Vector3::CreateZero() } }; + mockSurface->m_surfacePoints[AZStd::make_pair(1.0f, 1.0f)] = + AZStd::span(&mockOutputs[3], 1); ActivateEntity(surfaceEntity.get()); // We set the min/max to -5 - 15. By using a range without 0 at either end, and not having 0 as the midpoint, @@ -689,9 +725,12 @@ namespace UnitTest { for (int x = 0; x < dataSize; x++) { + point.m_position = AZ::Vector3(aznumeric_cast(x), aznumeric_cast(y), 0.0f); + point.m_normal = AZ::Vector3::CreateAxisZ(); point.m_surfaceTags.clear(); point.m_surfaceTags.emplace_back(AZ_CRC_CE("test_mask"), expectedOutput[(y * dataSize) + x]); - mockSurface->m_surfacePoints[AZStd::make_pair(static_cast(x), static_cast(y))] = { { point } }; + mockSurface->m_surfacePoints[AZStd::make_pair(static_cast(x), static_cast(y))] = + AZStd::span(&point, 1); } } ActivateEntity(surfaceEntity.get()); diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalSurfaceTests.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalSurfaceTests.cpp index 4ab4c76d56..45bf818c71 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalSurfaceTests.cpp +++ b/Gems/GradientSignal/Code/Tests/GradientSignalSurfaceTests.cpp @@ -108,12 +108,13 @@ namespace UnitTest EXPECT_TRUE(modifierHandle != SurfaceData::InvalidSurfaceDataRegistryHandle); // Call ModifySurfacePoints and verify the results - SurfaceData::SurfacePointList pointList = { { input } }; + SurfaceData::SurfacePointList pointList; + pointList.StartListConstruction(AZStd::span(&input, 1)); SurfaceData::SurfaceDataModifierRequestBus::Event(modifierHandle, &SurfaceData::SurfaceDataModifierRequestBus::Events::ModifySurfacePoints, pointList); + pointList.EndListConstruction(); ASSERT_EQ(pointList.GetSize(), 1); - pointList.EnumeratePoints( - [this, expectedOutput]( - const AZ::Vector3& position, const AZ::Vector3& normal, + pointList.EnumeratePoints([this, expectedOutput]( + [[maybe_unused]] size_t inPositionIndex, const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) { EXPECT_TRUE(SurfacePointsAreEqual(position, normal, masks, expectedOutput)); diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp index 8d5ada5214..2115d878c6 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp +++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp @@ -75,6 +75,22 @@ namespace UnitTest }); } + void GradientSignalTestEnvironment::PostCreateApplication() + { + // Ebus usage will allocate a global context on first usage. If that first usage occurs in a DLL, then the context will be + // invalid on subsequent unit test runs if using gtest_repeat. However, if we force the ebus to create their global context in + // the main test DLL (this one), the context will remain active throughout repeated runs. By creating them in + // PostCreateApplication(), they will be created before the DLLs get loaded and any system components from those DLLs run, so we + // can guarantee this will be the first usage. + + // These ebuses need their contexts created here before any of the dependent DLLs get loaded: + AZ::AssetTypeInfoBus::GetOrCreateContext(); + SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(); + SurfaceData::SurfaceDataProviderRequestBus::GetOrCreateContext(); + SurfaceData::SurfaceDataModifierRequestBus::GetOrCreateContext(); + LmbrCentral::ShapeComponentRequestsBus::GetOrCreateContext(); + } + void GradientSignalBaseFixture::SetupCoreSystems() { // Using the AZ::RPI::MakeAssetHandler will both create the asset handlers, diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h index 4d2233528b..486cdd6cc0 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h +++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h @@ -21,6 +21,7 @@ namespace UnitTest { public: void AddGemsAndComponents() override; + void PostCreateApplication() override; }; #ifdef HAVE_BENCHMARK diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestHelpers.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalTestHelpers.cpp index d3c537ea1d..c05d34221b 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTestHelpers.cpp +++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestHelpers.cpp @@ -259,7 +259,7 @@ namespace UnitTest const float width = aznumeric_cast(queryRange); // Call GetValue() on the EBus for every height and width in our ranges. - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (float y = 0.0f; y < height; y += 1.0f) { @@ -285,7 +285,7 @@ namespace UnitTest int64_t totalQueryPoints = queryRange * queryRange; // Call GetValues() for every height and width in our ranges. - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { // Set up our vector of query positions. This is done inside the benchmark timing since we're counting the work to create // each query position in the single GetValue() call benchmarks, and will make the timing more directly comparable. @@ -313,7 +313,7 @@ namespace UnitTest const float width = aznumeric_cast(queryRange); // Call GetValue() through the GradientSampler for every height and width in our ranges. - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (float y = 0.0f; y < height; y += 1.0f) { @@ -343,7 +343,7 @@ namespace UnitTest const int64_t totalQueryPoints = queryRange * queryRange; // Call GetValues() through the GradientSampler for every height and width in our ranges. - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { // Set up our vector of query positions. This is done inside the benchmark timing since we're counting the work to create // each query position in the single GetValue() call benchmarks, and will make the timing more directly comparable. diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h b/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h index c8e69e0d51..466eca2a07 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h +++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h @@ -132,6 +132,18 @@ namespace UnitTest providerRegistryEntry.m_bounds = m_bounds; providerRegistryEntry.m_tags = m_tags; + // Run through the set of surface points that have been set on this component to find out the maximum number + // that we'll return for any given input point. + providerRegistryEntry.m_maxPointsCreatedPerInput = 1; + for (auto& pointEntry : m_surfacePoints) + { + for (size_t index = 0; index < pointEntry.second.GetInputPositionSize(); index++) + { + providerRegistryEntry.m_maxPointsCreatedPerInput = + AZ::GetMax(providerRegistryEntry.m_maxPointsCreatedPerInput, pointEntry.second.GetSize(index)); + } + } + SurfaceData::SurfaceDataSystemRequestBus::BroadcastResult( m_providerHandle, &SurfaceData::SurfaceDataSystemRequestBus::Events::RegisterSurfaceDataProvider, providerRegistryEntry); SurfaceData::SurfaceDataProviderRequestBus::Handler::BusConnect(m_providerHandle); @@ -160,7 +172,15 @@ namespace UnitTest if (surfacePoints != m_surfacePoints.end()) { - surfacePointList = surfacePoints->second; + // If we have an entry for this input position, run through all of its points and add them to the passed-in list. + surfacePoints->second.EnumeratePoints( + [inPosition, &surfacePointList]( + [[maybe_unused]] size_t inPositionIndex, const AZ::Vector3& position, const AZ::Vector3& normal, + const SurfaceData::SurfaceTagWeights& weights) -> bool + { + surfacePointList.AddSurfacePoint(AZ::EntityId(), inPosition, position, normal, weights); + return true; + }); } } diff --git a/Gems/GradientSignal/Code/gradientsignal_editor_files.cmake b/Gems/GradientSignal/Code/gradientsignal_editor_files.cmake index b32e776b82..bde565bc69 100644 --- a/Gems/GradientSignal/Code/gradientsignal_editor_files.cmake +++ b/Gems/GradientSignal/Code/gradientsignal_editor_files.cmake @@ -15,8 +15,6 @@ set(FILES Source/UI/GradientPreviewDataWidget.h Source/UI/GradientPreviewWidget.cpp Source/UI/GradientPreviewWidget.h - Source/Editor/EditorImageProcessingSystemComponent.cpp - Source/Editor/EditorImageProcessingSystemComponent.h Source/Editor/EditorConstantGradientComponent.cpp Source/Editor/EditorConstantGradientComponent.h Source/Editor/EditorDitherGradientComponent.cpp @@ -25,8 +23,6 @@ set(FILES Source/Editor/EditorGradientSurfaceDataComponent.h Source/Editor/EditorGradientTransformComponent.cpp Source/Editor/EditorGradientTransformComponent.h - Source/Editor/EditorImageBuilderComponent.cpp - Source/Editor/EditorImageBuilderComponent.h Source/Editor/EditorImageGradientComponent.cpp Source/Editor/EditorImageGradientComponent.h Source/Editor/EditorInvertGradientComponent.cpp diff --git a/Gems/LmbrCentral/Code/Source/Scripting/EditorLookAtComponent.cpp b/Gems/LmbrCentral/Code/Source/Scripting/EditorLookAtComponent.cpp index 54a56cd2a6..00940178ac 100644 --- a/Gems/LmbrCentral/Code/Source/Scripting/EditorLookAtComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Scripting/EditorLookAtComponent.cpp @@ -33,6 +33,7 @@ namespace LmbrCentral ->Attribute(AZ::Edit::Attributes::Category, "Gameplay") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/LookAt.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/LookAt.svg") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/gameplay/look-at/") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) diff --git a/Gems/LmbrCentral/Code/Source/Scripting/EditorRandomTimedSpawnerComponent.cpp b/Gems/LmbrCentral/Code/Source/Scripting/EditorRandomTimedSpawnerComponent.cpp index cd45c88a8a..3ce49962e1 100644 --- a/Gems/LmbrCentral/Code/Source/Scripting/EditorRandomTimedSpawnerComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Scripting/EditorRandomTimedSpawnerComponent.cpp @@ -59,7 +59,6 @@ namespace LmbrCentral ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/RandomTimedSpawner.svg") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "http://docs.aws.amazon.com/console/lumberyard/userguide/random-timed-spawner-component") ->DataElement(AZ::Edit::UIHandlers::Default, &EditorRandomTimedSpawnerComponent::m_config, "m_config", "No Description") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) diff --git a/Gems/LmbrCentral/Code/Source/Scripting/EditorSpawnerComponent.cpp b/Gems/LmbrCentral/Code/Source/Scripting/EditorSpawnerComponent.cpp index f43b69f703..9f9b1cbb3c 100644 --- a/Gems/LmbrCentral/Code/Source/Scripting/EditorSpawnerComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Scripting/EditorSpawnerComponent.cpp @@ -41,7 +41,6 @@ namespace LmbrCentral ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Spawner.svg") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/spawner/") ->DataElement(0, &EditorSpawnerComponent::m_sliceAsset, "Dynamic slice", "The slice to spawn") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorSpawnerComponent::SliceAssetChanged) ->DataElement(0, &EditorSpawnerComponent::m_spawnOnActivate, "Spawn on activate", "Should the component spawn the selected slice upon activation?") diff --git a/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.cpp b/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.cpp index 7f919bc097..0b1848eb4b 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.cpp @@ -27,6 +27,7 @@ namespace LmbrCentral { incompatible.push_back(AZ_CRC_CE("ShapeService")); incompatible.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService")); + incompatible.push_back(AZ_CRC_CE("NonUniformScaleService")); } void AxisAlignedBoxShapeComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) @@ -34,11 +35,6 @@ namespace LmbrCentral required.push_back(AZ_CRC_CE("TransformService")); } - void AxisAlignedBoxShapeComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) - { - dependent.push_back(AZ_CRC_CE("NonUniformScaleService")); - } - void AxisAlignedBoxShapeDebugDisplayComponent::Reflect(AZ::ReflectContext* context) { if (auto serializeContext = azrtti_cast(context)) @@ -54,8 +50,6 @@ namespace LmbrCentral { EntityDebugDisplayComponent::Activate(); ShapeComponentNotificationsBus::Handler::BusConnect(GetEntityId()); - m_nonUniformScale = AZ::Vector3::CreateOne(); - AZ::NonUniformScaleRequestBus::EventResult(m_nonUniformScale, GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); } void AxisAlignedBoxShapeDebugDisplayComponent::Deactivate() @@ -74,7 +68,7 @@ namespace LmbrCentral transform.SetRotation(AZ::Quaternion::CreateIdentity()); saveMatrix = debugDisplay.PopPremultipliedMatrix(); debugDisplay.PushMatrix(transform); - DrawBoxShape(drawParams, m_boxShapeConfig, debugDisplay, m_nonUniformScale); + DrawBoxShape(drawParams, m_boxShapeConfig, debugDisplay); debugDisplay.PopMatrix(); debugDisplay.PushPremultipliedMatrix(saveMatrix); } @@ -104,7 +98,6 @@ namespace LmbrCentral if (changeReason == ShapeChangeReasons::ShapeChanged) { BoxShapeComponentRequestsBus::EventResult(m_boxShapeConfig, GetEntityId(), &BoxShapeComponentRequests::GetBoxConfiguration); - AZ::NonUniformScaleRequestBus::EventResult(m_nonUniformScale, GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); } } diff --git a/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.h b/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.h index e180e1d536..9e6060914f 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.h +++ b/Gems/LmbrCentral/Code/Source/Shape/AxisAlignedBoxShapeComponent.h @@ -33,7 +33,6 @@ namespace LmbrCentral static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); - static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); AxisAlignedBoxShape m_aaboxShape; ///< Stores underlying box type for this component. }; @@ -65,6 +64,5 @@ namespace LmbrCentral void OnShapeChanged(ShapeChangeReasons changeReason) override; BoxShapeConfig m_boxShapeConfig; ///< Stores configuration data for box shape. - AZ::Vector3 m_nonUniformScale = AZ::Vector3::CreateOne(); ///< Caches non-uniform scale for this entity. }; } // namespace LmbrCentral diff --git a/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.cpp b/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.cpp index f78f2f048d..ca16dbb58c 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.cpp @@ -89,9 +89,10 @@ namespace LmbrCentral provided.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService")); } - void EditorAxisAlignedBoxShapeComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + void EditorAxisAlignedBoxShapeComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { - dependent.push_back(AZ_CRC_CE("NonUniformScaleService")); + EditorBaseShapeComponent::GetIncompatibleServices(incompatible); + incompatible.push_back(AZ_CRC_CE("NonUniformScaleService")); } void EditorAxisAlignedBoxShapeComponent::DisplayEntityViewport( @@ -104,7 +105,7 @@ namespace LmbrCentral { DrawBoxShape( { m_aaboxShape.GetBoxConfiguration().GetDrawColor(), m_shapeWireColor, m_aaboxShape.GetBoxConfiguration().IsFilled() }, - m_aaboxShape.GetBoxConfiguration(), debugDisplay, m_aaboxShape.GetCurrentNonUniformScale()); + m_aaboxShape.GetBoxConfiguration(), debugDisplay); }, m_aaboxShape.GetCurrentTransform()); } @@ -161,8 +162,13 @@ namespace LmbrCentral return AzToolsFramework::TransformNormalizedScale(m_aaboxShape.GetCurrentTransform()); } + AZ::Transform EditorAxisAlignedBoxShapeComponent::GetCurrentLocalTransform() + { + return AZ::Transform::CreateIdentity(); + } + AZ::Vector3 EditorAxisAlignedBoxShapeComponent::GetBoxScale() { - return AZ::Vector3(m_aaboxShape.GetCurrentTransform().GetUniformScale() * m_aaboxShape.GetCurrentNonUniformScale()); + return AZ::Vector3(m_aaboxShape.GetCurrentTransform().GetUniformScale()); } } // namespace LmbrCentral diff --git a/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.h b/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.h index 8bff4ea7e1..346ed8c2c4 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.h +++ b/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.h @@ -38,7 +38,7 @@ namespace LmbrCentral protected: static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); - static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); // EditorComponentBase void BuildGameEntity(AZ::Entity* gameEntity) override; @@ -58,6 +58,7 @@ namespace LmbrCentral AZ::Vector3 GetDimensions() override; void SetDimensions(const AZ::Vector3& dimensions) override; AZ::Transform GetCurrentTransform() override; + AZ::Transform GetCurrentLocalTransform() override; AZ::Vector3 GetBoxScale() override; void ConfigurationChanged(); diff --git a/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp b/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp index 2983ca7753..777e2f728b 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp @@ -167,6 +167,11 @@ namespace LmbrCentral return AzToolsFramework::TransformNormalizedScale(m_boxShape.GetCurrentTransform()); } + AZ::Transform EditorBoxShapeComponent::GetCurrentLocalTransform() + { + return AZ::Transform::CreateIdentity(); + } + AZ::Vector3 EditorBoxShapeComponent::GetBoxScale() { return AZ::Vector3(m_boxShape.GetCurrentTransform().GetUniformScale() * m_boxShape.GetCurrentNonUniformScale()); diff --git a/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.h b/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.h index aa24bda5c4..499a20497c 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.h +++ b/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.h @@ -57,6 +57,7 @@ namespace LmbrCentral AZ::Vector3 GetDimensions() override; void SetDimensions(const AZ::Vector3& dimensions) override; AZ::Transform GetCurrentTransform() override; + AZ::Transform GetCurrentLocalTransform() override; AZ::Vector3 GetBoxScale() override; void ConfigurationChanged(); diff --git a/Gems/LmbrCentral/Code/Tests/AxisAlignedBoxShapeTest.cpp b/Gems/LmbrCentral/Code/Tests/AxisAlignedBoxShapeTest.cpp index c3e8d06791..a0f3b9e7a9 100644 --- a/Gems/LmbrCentral/Code/Tests/AxisAlignedBoxShapeTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/AxisAlignedBoxShapeTest.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -26,7 +25,6 @@ namespace UnitTest AZStd::unique_ptr m_transformComponentDescriptor; AZStd::unique_ptr m_axisAlignedBoxShapeComponentDescriptor; AZStd::unique_ptr m_axisAlignedBoxShapeDebugDisplayComponentDescriptor; - AZStd::unique_ptr m_nonUniformScaleComponentDescriptor; public: void SetUp() override @@ -43,9 +41,6 @@ namespace UnitTest m_axisAlignedBoxShapeDebugDisplayComponentDescriptor = AZStd::unique_ptr(LmbrCentral::AxisAlignedBoxShapeDebugDisplayComponent::CreateDescriptor()); m_axisAlignedBoxShapeDebugDisplayComponentDescriptor->Reflect(&(*m_serializeContext)); - m_nonUniformScaleComponentDescriptor = - AZStd::unique_ptr(AzFramework::NonUniformScaleComponent::CreateDescriptor()); - m_nonUniformScaleComponentDescriptor->Reflect(&(*m_serializeContext)); } void TearDown() override @@ -53,7 +48,6 @@ namespace UnitTest m_transformComponentDescriptor.reset(); m_axisAlignedBoxShapeComponentDescriptor.reset(); m_axisAlignedBoxShapeDebugDisplayComponentDescriptor.reset(); - m_nonUniformScaleComponentDescriptor.reset(); m_serializeContext.reset(); AllocatorsFixture::TearDown(); } @@ -73,23 +67,6 @@ namespace UnitTest entity.GetId(), &LmbrCentral::BoxShapeComponentRequestsBus::Events::SetBoxDimensions, dimensions); } - void CreateAxisAlignedBoxWithNonUniformScale( - const AZ::Transform& transform, const AZ::Vector3& nonUniformScale, const AZ::Vector3& dimensions, AZ::Entity& entity) - { - entity.CreateComponent(); - entity.CreateComponent(); - entity.CreateComponent(); - entity.CreateComponent(); - - entity.Init(); - entity.Activate(); - - AZ::TransformBus::Event(entity.GetId(), &AZ::TransformBus::Events::SetWorldTM, transform); - LmbrCentral::BoxShapeComponentRequestsBus::Event( - entity.GetId(), &LmbrCentral::BoxShapeComponentRequestsBus::Events::SetBoxDimensions, dimensions); - AZ::NonUniformScaleRequestBus::Event(entity.GetId(), &AZ::NonUniformScaleRequests::SetScale, nonUniformScale); - } - void CreateDefaultAxisAlignedBox(const AZ::Transform& transform, AZ::Entity& entity) { CreateAxisAlignedBox(transform, AZ::Vector3(10.0f, 10.0f, 10.0f), entity); @@ -187,52 +164,4 @@ namespace UnitTest EXPECT_TRUE(rayHit); EXPECT_NEAR(distance, 4.0f, 1e-2f); } - - TEST_F(AxisAlignedBoxShapeTest, RayIntersectWithBoxRotatedNonUniformScale) - { - AZ::Entity entity; - CreateAxisAlignedBoxWithNonUniformScale( - AZ::Transform( - AZ::Vector3(2.0f, -5.0f, 3.0f), AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::Constants::QuarterPi), - 0.5f), - AZ::Vector3(2.2f, 1.8f, 0.4f), AZ::Vector3(0.2f, 2.6f, 1.2f), entity); - - // This test creates a box of dimensions (2.2, 1.8, 0.4) centered on (2.0, -5, 3) and rotated about the Y axis by 45 degrees. - // The box is tested for axis-alignment by firing various rays and ensuring they either hit or miss the box. Any failure here - // would show the box has been rotated. - - // Ray should just miss the box - bool rayHit = false; - float distance = AZ::Constants::FloatMax; - LmbrCentral::ShapeComponentRequestsBus::EventResult( - rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, AZ::Vector3(1.8f, -6.2f, 3.0f), - AZ::Vector3(1.0f, 0.0f, 0.0f), distance); - EXPECT_FALSE(rayHit); - - // Ray should just hit the box - rayHit = false; - distance = AZ::Constants::FloatMax; - LmbrCentral::ShapeComponentRequestsBus::EventResult( - rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, AZ::Vector3(1.8f, -6.1f, 3.0f), - AZ::Vector3(1.0f, 0.0f, 0.0f), distance); - EXPECT_TRUE(rayHit); - EXPECT_NEAR(distance, 0.09f, 1e-3f); - - // Ray should just miss the box - rayHit = false; - distance = AZ::Constants::FloatMax; - LmbrCentral::ShapeComponentRequestsBus::EventResult( - rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, AZ::Vector3(2.2f, -6.2f, 3.0f), - AZ::Vector3(0.0f, 1.0f, 0.0f), distance); - EXPECT_FALSE(rayHit); - - // Ray should just hit the box - rayHit = false; - distance = AZ::Constants::FloatMax; - LmbrCentral::ShapeComponentRequestsBus::EventResult( - rayHit, entity.GetId(), &LmbrCentral::ShapeComponentRequests::IntersectRay, AZ::Vector3(2.1f, -6.2f, 3.0f), - AZ::Vector3(0.0f, 1.0f, 0.0f), distance); - EXPECT_TRUE(rayHit); - EXPECT_NEAR(distance, 0.03f, 1e-3f); - } } // namespace UnitTest diff --git a/Gems/LmbrCentral/Code/Tests/EditorBoxShapeComponentTests.cpp b/Gems/LmbrCentral/Code/Tests/EditorBoxShapeComponentTests.cpp index 4b72a4fb58..82a7f57937 100644 --- a/Gems/LmbrCentral/Code/Tests/EditorBoxShapeComponentTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/EditorBoxShapeComponentTests.cpp @@ -7,6 +7,17 @@ */ #include "LmbrCentralReflectionTest.h" #include "Shape/EditorBoxShapeComponent.h" +#include "Shape/EditorSphereShapeComponent.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace LmbrCentral { @@ -56,5 +67,110 @@ namespace LmbrCentral EXPECT_EQ(dimensions, AZ::Vector3(0.37f, 0.57f, 0.66f)); } + + class EditorBoxShapeComponentFixture : public UnitTest::ToolsApplicationFixture + { + public: + void SetUpEditorFixtureImpl() override; + void TearDownEditorFixtureImpl() override; + + AZStd::unique_ptr m_editorBoxShapeComponentDescriptor; + AZStd::unique_ptr m_editorSphereShapeComponentDescriptor; + + AZ::Entity* m_entity = nullptr; + }; + + void EditorBoxShapeComponentFixture::SetUpEditorFixtureImpl() + { + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + + // need to reflect EditorSphereShapeComponent in order for EditorBaseShapeComponent to be reflected + m_editorSphereShapeComponentDescriptor = AZStd::unique_ptr(EditorSphereShapeComponent::CreateDescriptor()); + + m_editorBoxShapeComponentDescriptor = + AZStd::unique_ptr(EditorBoxShapeComponent::CreateDescriptor()); + + ShapeComponentConfig::Reflect(serializeContext); + BoxShape::Reflect(serializeContext); + m_editorSphereShapeComponentDescriptor->Reflect(serializeContext); + m_editorBoxShapeComponentDescriptor->Reflect(serializeContext); + + UnitTest::CreateDefaultEditorEntity("BoxShapeComponentEntity", &m_entity); + m_entity->Deactivate(); + m_entity->CreateComponent(AzToolsFramework::Components::EditorNonUniformScaleComponent::RTTI_Type()); + m_entity->CreateComponent(EditorBoxShapeComponentTypeId); + m_entity->Activate(); + } + + void EditorBoxShapeComponentFixture::TearDownEditorFixtureImpl() + { + AzToolsFramework::EditorEntityContextRequestBus::Broadcast( + &AzToolsFramework::EditorEntityContextRequestBus::Events::DestroyEditorEntity, m_entity->GetId()); + m_entity = nullptr; + + m_editorBoxShapeComponentDescriptor.reset(); + m_editorSphereShapeComponentDescriptor.reset(); + } + + using EditorBoxShapeComponentManipulatorFixture = + UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin; + + TEST_F(EditorBoxShapeComponentManipulatorFixture, BoxShapeNonUniformScaleManipulatorsScaleCorrectly) + { + // a rotation which rotates the x-axis to (0.8, 0.6, 0) + const AZ::Quaternion boxRotation(0.0f, 0.0f, 0.316228f, 0.948683f); + AZ::Transform boxTransform = AZ::Transform::CreateFromQuaternionAndTranslation(boxRotation, AZ::Vector3(2.0f, 3.0f, 4.0f)); + boxTransform.SetUniformScale(1.5f); + AZ::TransformBus::Event(m_entity->GetId(), &AZ::TransformBus::Events::SetWorldTM, boxTransform); + + const AZ::Vector3 nonUniformScale(4.0f, 1.5f, 2.0f); + AZ::NonUniformScaleRequestBus::Event(m_entity->GetId(), &AZ::NonUniformScaleRequests::SetScale, nonUniformScale); + + const AZ::Vector3 boxDimensions(1.0f, 2.0f, 2.5f); + BoxShapeComponentRequestsBus::Event(m_entity->GetId(), &BoxShapeComponentRequests::SetBoxDimensions, boxDimensions); + + // enter the box shape component's component mode + AzToolsFramework::SelectEntity(m_entity->GetId()); + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( + &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Events::AddSelectedComponentModesOfType, + EditorBoxShapeComponentTypeId); + + // position the camera so it is looking down at the box + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(-AZ::Constants::HalfPi), AZ::Vector3(2.0f, 3.0f, 20.0f))); + + // position in world space which should allow grabbing the box's x scale manipulator + // the unscaled position of the x scale manipulator in the box's local frame should be (0.5f, 0.0f, 0.0f) + // after non-uniform scale, the manipulator position should be (2.0f, 0.0f, 0.0f) + // after the scale of the entity transform, the manipulator position should be (3.0f, 0.0f, 0.0f) + // after the rotation of the entity transform, the manipulator position should be (2.4f, 1.8f, 0.0f) + // after the translation of the entity transform, the manipulator position should be (4.4f, 4.8f, 4.0f) + const AZ::Vector3 worldStart(4.4f, 4.8f, 4.0f); + + // position in world space to move to + const AZ::Vector3 worldEnd(6.8f, 6.6f, 4.0f); + + const auto screenStart = AzFramework::WorldToScreen(worldStart, m_cameraState); + const auto screenEnd = AzFramework::WorldToScreen(worldEnd, m_cameraState); + + m_actionDispatcher + ->CameraState(m_cameraState) + // move the mouse to the position of the x scale manipulator + ->MousePosition(screenStart) + // drag to move the manipulator + ->MouseLButtonDown() + ->MousePosition(screenEnd) + ->MouseLButtonUp(); + + AZ::Vector3 newBoxDimensions = AZ::Vector3::CreateZero(); + BoxShapeComponentRequestsBus::EventResult(newBoxDimensions, m_entity->GetId(), &BoxShapeComponentRequests::GetBoxDimensions); + + const AZ::Vector3 expectedBoxDimensions(2.0f, 2.0f, 2.5f); + // allow a reasonably high tolerance because we can't get better accuracy than the resolution of the viewport + EXPECT_THAT(newBoxDimensions, UnitTest::IsCloseTolerance(expectedBoxDimensions, 1e-2f)); + } } diff --git a/Gems/LyShine/Code/Editor/Animation/Util/UiEditorUtils.cpp b/Gems/LyShine/Code/Editor/Animation/Util/UiEditorUtils.cpp index 85270df355..fb43e00780 100644 --- a/Gems/LyShine/Code/Editor/Animation/Util/UiEditorUtils.cpp +++ b/Gems/LyShine/Code/Editor/Animation/Util/UiEditorUtils.cpp @@ -9,6 +9,8 @@ //#include "CustomizeKeyboardPage.h" +#include + #include #include @@ -99,9 +101,9 @@ QColor ColorLinearToGamma(ColorF col) float g = clamp_tpl(col.g, 0.0f, 1.0f); float b = clamp_tpl(col.b, 0.0f, 1.0f); - r = (float)(r <= 0.0031308 ? (12.92 * r) : (1.055 * pow((double)r, 1.0 / 2.4) - 0.055)); - g = (float)(g <= 0.0031308 ? (12.92 * g) : (1.055 * pow((double)g, 1.0 / 2.4) - 0.055)); - b = (float)(b <= 0.0031308 ? (12.92 * b) : (1.055 * pow((double)b, 1.0 / 2.4) - 0.055)); + r = AZ::Color::ConvertSrgbLinearToGamma(r); + g = AZ::Color::ConvertSrgbLinearToGamma(g); + b = AZ::Color::ConvertSrgbLinearToGamma(b); return QColor(int(r * 255.0f), int(g * 255.0f), int(b * 255.0f)); } @@ -113,7 +115,7 @@ ColorF ColorGammaToLinear(const QColor& col) float g = (float)col.green() / 255.0f; float b = (float)col.blue() / 255.0f; - return ColorF((float)(r <= 0.04045 ? (r / 12.92) : pow(((double)r + 0.055) / 1.055, 2.4)), - (float)(g <= 0.04045 ? (g / 12.92) : pow(((double)g + 0.055) / 1.055, 2.4)), - (float)(b <= 0.04045 ? (b / 12.92) : pow(((double)b + 0.055) / 1.055, 2.4))); + return ColorF(AZ::Color::ConvertSrgbGammaToLinear(r), + AZ::Color::ConvertSrgbGammaToLinear(g), + AZ::Color::ConvertSrgbGammaToLinear(b)); } diff --git a/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp b/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp index e186e1158d..4003a4510d 100644 --- a/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp +++ b/Gems/LyShine/Code/Source/World/UiCanvasAssetRefComponent.cpp @@ -170,7 +170,7 @@ void UiCanvasAssetRefComponent::Reflect(AZ::ReflectContext* context) ->Attribute(AZ::Edit::Attributes::Category, "UI") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/UiCanvasAssetRef.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/UiCanvasAssetRef.svg") - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/ui-canvas-asset-ref/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/ui/canvas-asset-ref/") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)); editInfo->DataElement("SimpleAssetRef", &UiCanvasAssetRefComponent::m_canvasAssetRef, diff --git a/Gems/LyShine/Code/Source/World/UiCanvasProxyRefComponent.cpp b/Gems/LyShine/Code/Source/World/UiCanvasProxyRefComponent.cpp index 22e4207825..7c75113205 100644 --- a/Gems/LyShine/Code/Source/World/UiCanvasProxyRefComponent.cpp +++ b/Gems/LyShine/Code/Source/World/UiCanvasProxyRefComponent.cpp @@ -72,7 +72,7 @@ void UiCanvasProxyRefComponent::Reflect(AZ::ReflectContext* context) ->Attribute(AZ::Edit::Attributes::Category, "UI") ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/UiCanvasProxyRef.svg") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/UiCanvasProxyRef.svg") - ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/ui-canvas-proxy-ref/") + ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/ui/canvas-proxy-ref/") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c)); editInfo->DataElement(0, &UiCanvasProxyRefComponent::m_canvasAssetRefEntityId, diff --git a/Gems/Microphone/Code/Source/Platform/Mac/MicrophoneSystemComponent_Mac.mm b/Gems/Microphone/Code/Source/Platform/Mac/MicrophoneSystemComponent_Mac.mm index 567f2db6b2..fe556f6d07 100644 --- a/Gems/Microphone/Code/Source/Platform/Mac/MicrophoneSystemComponent_Mac.mm +++ b/Gems/Microphone/Code/Source/Platform/Mac/MicrophoneSystemComponent_Mac.mm @@ -84,7 +84,7 @@ public: AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster + kAudioObjectPropertyElementMain }; status = AudioObjectGetPropertyData(kAudioObjectSystemObject, diff --git a/Gems/MotionMatching/README.md b/Gems/MotionMatching/README.md index f07116fcd7..33d7f67276 100644 --- a/Gems/MotionMatching/README.md +++ b/Gems/MotionMatching/README.md @@ -112,6 +112,87 @@ The instance is where everything comes together. It stores the trajectory histor ## Architecture ![Class Diagram](https://user-images.githubusercontent.com/43751992/151819361-878edcb5-2b1f-4867-bb7f-8ed5c09a075a.png) +## The motion matching algorithm + +Motion matching plays small clips of a motion database, while jumping and smoothly transitioning back and forth, to synthesize a new animation from that data. + +### Update loop + +In the majority of the game ticks, the current motion gets advanced. A few times per second, the actual motion matching search is triggered to not drift away too far from the expected user input (as we would just play the recorded animation otherwise). + +When a search for a better next matching frame is triggered, the current pose, including its joint velocities, gets evaluated. This pose (which we'll call input or query pose) is used to fill the query vector. The query vector contains feature values and is compared against other frames in the feature matrix. The query vector has the same size as there are columns in the feature matrix and is similar to any other row but represents the query pose. + +Using the query vector, we can find the next best matching frame in the motion database and start transitioning towards that. + +In case the new best matching frame candidate is close to the time in the animation that we are already playing, we don't do anything as we seem to be at the sweet spot in the motion database already. + +Pseudo-code: +``` +// Keep playing the current animation. +currentMotion.Update(timeDelta); + +if (Is it time to search for a new best matching frame?) // We might e.g. do this 5x a second +{ + // Evaluate the current pose including joint velocities. + queryPose = SamplePose(newMotionTime); + + // Update the input query vector (Calculate features for the query pose) + queryValues = CalculateFeaturesFromPose(queryPose); + + // Find the frame with the lowest cost based on the query vector. + bestMatchingFrame = FindBestMatchingFrame(queryValues); + + // Start transitioning towards the new best matching frame in case it is not + // really close to the frame we are already playing. + if (IsClose(bestMatchingFrame, currentMotion.GetFrame()) == false) + { + StartTransition(bestMatchingFrame); + } +} +``` + +### Cost function + +The core question in the algorithm is: Where do we jump and transition to? The algorithm tries to find the best time in the motion database that matches the current character pose including its movements and the user input. To compare the frame candidates with each other, we use a cost function. + +The feature schema defines the cost function. Every feature added to the feature schema adds up to the cost. The bigger the discrepancy between e.g. the current velocity and the one from the frame candidate, the higher the penalty to the cost and the less likely the candidate is a good one to take. + +This makes motion matching an optimization problem where the frame with the minimum cost is the most preferred candidate to transition to. + +### Searching next best matching frame + +The actual search happens in two phases, a broad phase to eliminate most of the candidates followed by a narrow phase to find the actual best candidate. + +#### 1. Broad-phase (KD-tree) + +A KD-tree is used to find the nearest neighbors (frames in the motion database) to the query vector (given input). The result is a set of pre-selected frames for the next best matching frame that is passed on to the narrow-phase. By adjusting the maximum tree depth or the minimum number of frames for the leaf nodes, the resulting number of frames can be adjusted. The bigger the set of frames the broad-phase returns, the more candidates the narrow-phase can choose from, the better the visual quality of the animation but the slower the algorithm. + +#### 2. Narrow-phase + +Inside the narrow-phase, we iterate through the returned set of frames from the KD-tree, and evaluate and compare their cost against each other. The frame with the minimal cost is the best match that we transition to. + +Pseudo-code: +``` +minCost = MAX; +for_all (nearest frames found in the broad-phase) +{ + frameCost = 0.0 + for_all (features) + { + frameCost += CalculateCost(feature); + } + + if (frameCost < minCost) + { + // We found a better next matching frame + minCost = frameCost; + newBestMatchingFrame = currentFrame; + } +} + +StartTransition(newBestMatchingFrame); +``` + ## Jupyter notebook ### Feature histograms diff --git a/Gems/PhysX/Code/Editor/ColliderCapsuleMode.cpp b/Gems/PhysX/Code/Editor/ColliderCapsuleMode.cpp index 0bc941b082..b704f8feec 100644 --- a/Gems/PhysX/Code/Editor/ColliderCapsuleMode.cpp +++ b/Gems/PhysX/Code/Editor/ColliderCapsuleMode.cpp @@ -8,11 +8,14 @@ #include "ColliderCapsuleMode.h" #include +#include #include #include #include +#include #include +#include #include #include @@ -34,10 +37,15 @@ namespace PhysX void ColliderCapsuleMode::Setup(const AZ::EntityComponentIdPair& idPair) { AZ::Transform colliderWorldTransform = AZ::Transform::Identity(); - PhysX::EditorColliderComponentRequestBus::EventResult(colliderWorldTransform, idPair, &PhysX::EditorColliderComponentRequests::GetColliderWorldTransform); + AZ::TransformBus::EventResult(colliderWorldTransform, idPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); - SetupRadiusManipulator(idPair, colliderWorldTransform); - SetupHeightManipulator(idPair, colliderWorldTransform); + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, idPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + + SetupRadiusManipulator(idPair, colliderWorldTransform, colliderLocalTransform, nonUniformScale); + SetupHeightManipulator(idPair, colliderWorldTransform, colliderLocalTransform, nonUniformScale); AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(idPair.GetEntityId()); } @@ -45,7 +53,12 @@ namespace PhysX void ColliderCapsuleMode::Refresh(const AZ::EntityComponentIdPair& idPair) { AZ::Transform colliderWorldTransform = AZ::Transform::Identity(); - PhysX::EditorColliderComponentRequestBus::EventResult(colliderWorldTransform, idPair, &PhysX::EditorColliderComponentRequests::GetColliderWorldTransform); + AZ::TransformBus::EventResult(colliderWorldTransform, idPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); + + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, idPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + + AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); // Read the state of the capsule into manipulators to support undo/redo float capsuleHeight = 0.0f; @@ -55,9 +68,13 @@ namespace PhysX PhysX::EditorColliderComponentRequestBus::EventResult(capsuleRadius, idPair, &PhysX::EditorColliderComponentRequests::GetCapsuleRadius); m_radiusManipulator->SetSpace(colliderWorldTransform); - m_radiusManipulator->SetLocalPosition(m_radiusManipulator->GetAxis() * capsuleRadius); + m_radiusManipulator->SetLocalTransform( + colliderLocalTransform * AZ::Transform::CreateTranslation(m_radiusManipulator->GetAxis() * capsuleRadius)); + m_radiusManipulator->SetNonUniformScale(nonUniformScale); m_heightManipulator->SetSpace(colliderWorldTransform); - m_heightManipulator->SetLocalPosition(m_heightManipulator->GetAxis() * capsuleHeight * HalfHeight); + m_heightManipulator->SetLocalTransform( + colliderLocalTransform * AZ::Transform::CreateTranslation(m_heightManipulator->GetAxis() * capsuleHeight * HalfHeight)); + m_heightManipulator->SetNonUniformScale(nonUniformScale); } void ColliderCapsuleMode::Teardown(const AZ::EntityComponentIdPair& idPair) @@ -83,13 +100,23 @@ namespace PhysX AZ_UNUSED(debugDisplay); const AzFramework::CameraState cameraState = AzToolsFramework::GetCameraState(viewportInfo.m_viewportId); - float radius = m_radiusManipulator->GetLocalPosition().GetLength(); - m_radiusManipulator->SetAxis(cameraState.m_side); - m_radiusManipulator->SetLocalPosition(cameraState.m_side * radius); + if (!m_radiusManipulator->EntityComponentIdPairs().empty()) + { + const AZ::EntityComponentIdPair idPair = *m_radiusManipulator->EntityComponentIdPairs().begin(); + float radius = 0.0f; + PhysX::EditorColliderComponentRequestBus::EventResult(radius, idPair, &PhysX::EditorColliderComponentRequests::GetCapsuleRadius); + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + m_radiusManipulator->SetAxis(cameraState.m_side); + m_radiusManipulator->SetLocalTransform(colliderLocalTransform * AZ::Transform::CreateTranslation(cameraState.m_side * radius)); + } } - void ColliderCapsuleMode::SetupRadiusManipulator(const AZ::EntityComponentIdPair& idPair, const AZ::Transform& worldTransform) + void ColliderCapsuleMode::SetupRadiusManipulator( + const AZ::EntityComponentIdPair& idPair, + const AZ::Transform& worldTransform, + const AZ::Transform& localTransform, + const AZ::Vector3& nonUniformScale) { // Radius manipulator float capsuleRadius = 0.0f; @@ -99,7 +126,8 @@ namespace PhysX m_radiusManipulator->AddEntityComponentIdPair(idPair); m_radiusManipulator->SetAxis(RadiusManipulatorAxis); m_radiusManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - m_radiusManipulator->SetLocalPosition(RadiusManipulatorAxis * capsuleRadius); + m_radiusManipulator->SetLocalTransform(localTransform * AZ::Transform::CreateTranslation(RadiusManipulatorAxis * capsuleRadius)); + m_radiusManipulator->SetNonUniformScale(nonUniformScale); { AzToolsFramework::ManipulatorViews views; @@ -114,7 +142,11 @@ namespace PhysX }); } - void ColliderCapsuleMode::SetupHeightManipulator(const AZ::EntityComponentIdPair& idPair, const AZ::Transform& worldTransform) + void ColliderCapsuleMode::SetupHeightManipulator( + const AZ::EntityComponentIdPair& idPair, + const AZ::Transform& worldTransform, + const AZ::Transform& localTransform, + const AZ::Vector3& nonUniformScale) { // Height manipulator float capsuleHeight = 0.0f; @@ -124,7 +156,9 @@ namespace PhysX m_heightManipulator->AddEntityComponentIdPair(idPair); m_heightManipulator->SetAxis(HeightManipulatorAxis); m_heightManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - m_heightManipulator->SetLocalPosition(HeightManipulatorAxis * capsuleHeight * HalfHeight); // Manipulator positioned at half the capsules height. + m_heightManipulator->SetLocalTransform( + localTransform * AZ::Transform::CreateTranslation(HeightManipulatorAxis * capsuleHeight * HalfHeight)); + m_heightManipulator->SetNonUniformScale(nonUniformScale); { AzToolsFramework::ManipulatorViews views; @@ -137,19 +171,23 @@ namespace PhysX { OnHeightManipulatorMoved(action, idPair); }); - } void ColliderCapsuleMode::OnRadiusManipulatorMoved(const AzToolsFramework::LinearManipulator::Action& action, const AZ::EntityComponentIdPair& idPair) { + // manipulator action offsets do not take entity transform scale into account, so need to apply it here + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + const AZ::Vector3 manipulatorPosition = AzToolsFramework::GetPositionInManipulatorFrame( + m_radiusManipulator->GetSpace().GetUniformScale(), colliderLocalTransform, action); + // Get the distance the manipulator has moved along the axis. - float extent = action.LocalPosition().Dot(action.m_fixed.m_axis); + float extent = manipulatorPosition.Dot(action.m_fixed.m_axis); // Clamp radius to a small value. extent = AZ::GetMax(extent, MinCapsuleRadius); // Update the manipulator and capsule radius. - m_radiusManipulator->SetLocalPosition(extent * action.m_fixed.m_axis); + m_radiusManipulator->SetLocalTransform(colliderLocalTransform * AZ::Transform::CreateTranslation(extent * action.m_fixed.m_axis)); // Adjust the height manipulator so it is always clamped to twice the radius. AdjustHeightManipulator(idPair, static_cast(extent)); @@ -160,14 +198,19 @@ namespace PhysX void ColliderCapsuleMode::OnHeightManipulatorMoved(const AzToolsFramework::LinearManipulator::Action& action, const AZ::EntityComponentIdPair& idPair) { + // manipulator action offsets do not take entity transform scale into account, so need to apply it here + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + const AZ::Vector3 manipulatorPosition = AzToolsFramework::GetPositionInManipulatorFrame( + m_heightManipulator->GetSpace().GetUniformScale(), colliderLocalTransform, action); + // Get the distance the manipulator has moved along the axis. - float extent = action.LocalPosition().Dot(action.m_fixed.m_axis); + float extent = manipulatorPosition.Dot(action.m_fixed.m_axis); // Ensure capsule's half height is always greater than the radius. extent = AZ::GetMax(extent, MinCapsuleHeight); // Update the manipulator and capsule height. - m_heightManipulator->SetLocalPosition(extent * action.m_fixed.m_axis); + m_heightManipulator->SetLocalTransform(colliderLocalTransform * AZ::Transform::CreateTranslation(extent * action.m_fixed.m_axis)); // The final height of the capsule is twice the manipulator's extent. float capsuleHeight = extent / HalfHeight; @@ -188,7 +231,9 @@ namespace PhysX capsuleRadius = AZ::GetMin(capsuleRadius, capsuleHeight * HalfHeight); // Update manipulator and the capsule radius. - m_radiusManipulator->SetLocalPosition(capsuleRadius * m_radiusManipulator->GetAxis()); + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + m_radiusManipulator->SetLocalTransform( + colliderLocalTransform * AZ::Transform::CreateTranslation(capsuleRadius * m_radiusManipulator->GetAxis())); PhysX::EditorColliderComponentRequestBus::Event(idPair, &PhysX::EditorColliderComponentRequests::SetCapsuleRadius, capsuleRadius); } @@ -201,7 +246,9 @@ namespace PhysX capsuleHeight = AZ::GetMax(capsuleHeight, capsuleRadius / HalfHeight); // Update the manipulator and capsule height. - m_heightManipulator->SetLocalPosition(capsuleHeight * HalfHeight * m_heightManipulator->GetAxis()); + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + m_heightManipulator->SetLocalTransform( + colliderLocalTransform * AZ::Transform::CreateTranslation(capsuleHeight * HalfHeight * m_heightManipulator->GetAxis())); PhysX::EditorColliderComponentRequestBus::Event(idPair, &PhysX::EditorColliderComponentRequests::SetCapsuleHeight, capsuleHeight); } } diff --git a/Gems/PhysX/Code/Editor/ColliderCapsuleMode.h b/Gems/PhysX/Code/Editor/ColliderCapsuleMode.h index ceedd84a35..5284b3d1ed 100644 --- a/Gems/PhysX/Code/Editor/ColliderCapsuleMode.h +++ b/Gems/PhysX/Code/Editor/ColliderCapsuleMode.h @@ -34,8 +34,16 @@ namespace PhysX const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; - void SetupRadiusManipulator(const AZ::EntityComponentIdPair& idPair, const AZ::Transform& worldTransform); - void SetupHeightManipulator(const AZ::EntityComponentIdPair& idPair, const AZ::Transform& worldTransform); + void SetupRadiusManipulator( + const AZ::EntityComponentIdPair& idPair, + const AZ::Transform& worldTransform, + const AZ::Transform& localTransform, + const AZ::Vector3& nonUniformScale); + void SetupHeightManipulator( + const AZ::EntityComponentIdPair& idPair, + const AZ::Transform& worldTransform, + const AZ::Transform& localTransform, + const AZ::Vector3& nonUniformScale); void OnRadiusManipulatorMoved(const AzToolsFramework::LinearManipulator::Action& action, const AZ::EntityComponentIdPair& idPair); void OnHeightManipulatorMoved(const AzToolsFramework::LinearManipulator::Action& action, const AZ::EntityComponentIdPair& idPair); void AdjustRadiusManipulator(const AZ::EntityComponentIdPair& idPair, const float capsuleHeight); diff --git a/Gems/PhysX/Code/Editor/ColliderOffsetMode.cpp b/Gems/PhysX/Code/Editor/ColliderOffsetMode.cpp index 21f27e4622..2c9b90d5b3 100644 --- a/Gems/PhysX/Code/Editor/ColliderOffsetMode.cpp +++ b/Gems/PhysX/Code/Editor/ColliderOffsetMode.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -28,10 +29,14 @@ namespace PhysX AZ::Transform worldTransform; AZ::TransformBus::EventResult(worldTransform, idPair.GetEntityId(), &AZ::TransformInterface::GetWorldTM); + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, idPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + AZ::Vector3 colliderOffset; PhysX::EditorColliderComponentRequestBus::EventResult(colliderOffset, idPair, &PhysX::EditorColliderComponentRequests::GetColliderOffset); m_translationManipulators.SetSpace(worldTransform); + m_translationManipulators.SetNonUniformScale(nonUniformScale); m_translationManipulators.SetLocalPosition(colliderOffset); m_translationManipulators.AddEntityComponentIdPair(idPair); m_translationManipulators.Register(AzToolsFramework::g_mainManipulatorManagerId); @@ -40,19 +45,19 @@ namespace PhysX m_translationManipulators.InstallLinearManipulatorMouseMoveCallback([this, idPair]( const AzToolsFramework::LinearManipulator::Action& action) { - OnManipulatorMoved(action.LocalPosition(), idPair); + OnManipulatorMoved(action.m_start.m_localPosition, action.m_current.m_localPositionOffset, idPair); }); m_translationManipulators.InstallPlanarManipulatorMouseMoveCallback([this, idPair]( const AzToolsFramework::PlanarManipulator::Action& action) { - OnManipulatorMoved(action.LocalPosition(), idPair); + OnManipulatorMoved(action.m_start.m_localPosition, action.m_current.m_localOffset, idPair); }); m_translationManipulators.InstallSurfaceManipulatorMouseMoveCallback([this, idPair]( const AzToolsFramework::SurfaceManipulator::Action& action) { - OnManipulatorMoved(action.LocalPosition(), idPair); + OnManipulatorMoved(action.m_start.m_localPosition, action.m_current.m_localOffset, idPair); }); } @@ -69,10 +74,14 @@ namespace PhysX m_translationManipulators.Unregister(); } - void ColliderOffsetMode::OnManipulatorMoved(const AZ::Vector3& position, const AZ::EntityComponentIdPair& idPair) + void ColliderOffsetMode::OnManipulatorMoved(const AZ::Vector3& startPosition, const AZ::Vector3& offset, const AZ::EntityComponentIdPair& idPair) { - m_translationManipulators.SetLocalPosition(position); - PhysX::EditorColliderComponentRequestBus::Event(idPair, &PhysX::EditorColliderComponentRequests::SetColliderOffset, position); + AZ::Transform worldTransform = AZ::Transform::CreateIdentity(); + AZ::TransformBus::EventResult(worldTransform, idPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); + const float scale = AZ::GetMax(AZ::MinTransformScale, worldTransform.GetUniformScale()); + const AZ::Vector3 newPosition = startPosition + offset / scale; + m_translationManipulators.SetLocalPosition(newPosition); + PhysX::EditorColliderComponentRequestBus::Event(idPair, &PhysX::EditorColliderComponentRequests::SetColliderOffset, newPosition); } void ColliderOffsetMode::ResetValues(const AZ::EntityComponentIdPair& idPair) diff --git a/Gems/PhysX/Code/Editor/ColliderOffsetMode.h b/Gems/PhysX/Code/Editor/ColliderOffsetMode.h index 6a2e6a8a12..525000a1b1 100644 --- a/Gems/PhysX/Code/Editor/ColliderOffsetMode.h +++ b/Gems/PhysX/Code/Editor/ColliderOffsetMode.h @@ -28,7 +28,8 @@ namespace PhysX void ResetValues(const AZ::EntityComponentIdPair& idPair) override; private: - void OnManipulatorMoved(const AZ::Vector3& position, const AZ::EntityComponentIdPair& idPair); + void OnManipulatorMoved( + const AZ::Vector3& startPosition, const AZ::Vector3& offset, const AZ::EntityComponentIdPair& idPair); AzToolsFramework::TranslationManipulators m_translationManipulators; }; diff --git a/Gems/PhysX/Code/Editor/ColliderRotationMode.cpp b/Gems/PhysX/Code/Editor/ColliderRotationMode.cpp index 5f3c98bcee..d39fd001e4 100644 --- a/Gems/PhysX/Code/Editor/ColliderRotationMode.cpp +++ b/Gems/PhysX/Code/Editor/ColliderRotationMode.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,9 @@ namespace PhysX AZ::Transform worldTransform; AZ::TransformBus::EventResult(worldTransform, idPair.GetEntityId(), &AZ::TransformInterface::GetWorldTM); + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, idPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + AZ::Quaternion colliderRotation; PhysX::EditorColliderComponentRequestBus::EventResult(colliderRotation, idPair, &PhysX::EditorColliderComponentRequests::GetColliderRotation); @@ -36,7 +40,8 @@ namespace PhysX PhysX::EditorColliderComponentRequestBus::EventResult(colliderOffset, idPair, &PhysX::EditorColliderComponentRequests::GetColliderOffset); m_rotationManipulators.SetSpace(worldTransform); - m_rotationManipulators.SetLocalPosition(colliderOffset); + m_rotationManipulators.SetNonUniformScale(nonUniformScale); + m_rotationManipulators.SetLocalPosition(nonUniformScale * colliderOffset); m_rotationManipulators.SetLocalOrientation(colliderRotation); m_rotationManipulators.AddEntityComponentIdPair(idPair); m_rotationManipulators.Register(AzToolsFramework::g_mainManipulatorManagerId); @@ -70,7 +75,11 @@ namespace PhysX AZ::Vector3 colliderOffset = AZ::Vector3::CreateZero(); PhysX::EditorColliderComponentRequestBus::EventResult(colliderOffset, idPair, &PhysX::EditorColliderComponentRequests::GetColliderOffset); - m_rotationManipulators.SetLocalPosition(colliderOffset); + + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, idPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + + m_rotationManipulators.SetLocalPosition(nonUniformScale * colliderOffset); } void ColliderRotationMode::Teardown(const AZ::EntityComponentIdPair& idPair) diff --git a/Gems/PhysX/Code/Editor/ColliderSphereMode.cpp b/Gems/PhysX/Code/Editor/ColliderSphereMode.cpp index ba0fd76ea9..8d69e28726 100644 --- a/Gems/PhysX/Code/Editor/ColliderSphereMode.cpp +++ b/Gems/PhysX/Code/Editor/ColliderSphereMode.cpp @@ -8,11 +8,14 @@ #include "ColliderSphereMode.h" #include +#include #include #include #include +#include #include +#include #include #include #include @@ -27,7 +30,12 @@ namespace PhysX void ColliderSphereMode::Setup(const AZ::EntityComponentIdPair& idPair) { AZ::Transform colliderWorldTransform = AZ::Transform::Identity(); - PhysX::EditorColliderComponentRequestBus::EventResult(colliderWorldTransform, idPair, &PhysX::EditorColliderComponentRequests::GetColliderWorldTransform); + AZ::TransformBus::EventResult(colliderWorldTransform, idPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); + + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, idPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); float sphereRadius = 0.0f; PhysX::EditorColliderComponentRequestBus::EventResult(sphereRadius, idPair, &PhysX::EditorColliderComponentRequests::GetSphereRadius); @@ -36,7 +44,9 @@ namespace PhysX m_radiusManipulator->AddEntityComponentIdPair(idPair); m_radiusManipulator->SetAxis(ManipulatorAxis); m_radiusManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - m_radiusManipulator->SetLocalPosition(ManipulatorAxis * sphereRadius); + m_radiusManipulator->SetLocalTransform(colliderLocalTransform * AZ::Transform::CreateTranslation(ManipulatorAxis * sphereRadius)); + m_radiusManipulator->SetNonUniformScale(nonUniformScale); + m_radiusManipulator->SetBoundsDirty(); AzToolsFramework::ManipulatorViews views; views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(AzFramework::ViewportColors::DefaultManipulatorHandleColor, AzFramework::ViewportConstants::DefaultManipulatorHandleSize)); @@ -53,12 +63,18 @@ namespace PhysX void ColliderSphereMode::Refresh(const AZ::EntityComponentIdPair& idPair) { AZ::Transform colliderWorldTransform = AZ::Transform::Identity(); - PhysX::EditorColliderComponentRequestBus::EventResult(colliderWorldTransform, idPair, &PhysX::EditorColliderComponentRequests::GetColliderWorldTransform); + AZ::TransformBus::EventResult(colliderWorldTransform, idPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); m_radiusManipulator->SetSpace(colliderWorldTransform); + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, idPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + float sphereRadius = 0.0f; PhysX::EditorColliderComponentRequestBus::EventResult(sphereRadius, idPair, &PhysX::EditorColliderComponentRequests::GetSphereRadius); - m_radiusManipulator->SetLocalPosition(ManipulatorAxis * sphereRadius); + m_radiusManipulator->SetLocalTransform(colliderLocalTransform * AZ::Transform::CreateTranslation(ManipulatorAxis * sphereRadius)); + m_radiusManipulator->SetBoundsDirty(); } void ColliderSphereMode::Teardown(const AZ::EntityComponentIdPair& idPair) @@ -80,22 +96,34 @@ namespace PhysX AZ_UNUSED(debugDisplay); const AzFramework::CameraState cameraState = AzToolsFramework::GetCameraState(viewportInfo.m_viewportId); - float radius = m_radiusManipulator->GetLocalPosition().GetLength(); - m_radiusManipulator->SetAxis(cameraState.m_side); - m_radiusManipulator->SetLocalPosition(cameraState.m_side * radius); + if (!m_radiusManipulator->EntityComponentIdPairs().empty()) + { + const AZ::EntityComponentIdPair idPair = *m_radiusManipulator->EntityComponentIdPairs().begin(); + float radius = 0.0f; + PhysX::EditorColliderComponentRequestBus::EventResult(radius, idPair, &PhysX::EditorColliderComponentRequests::GetSphereRadius); + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + m_radiusManipulator->SetAxis(cameraState.m_side); + m_radiusManipulator->SetLocalTransform(colliderLocalTransform * AZ::Transform::CreateTranslation(cameraState.m_side * radius)); + } } void ColliderSphereMode::OnManipulatorMoved(const AzToolsFramework::LinearManipulator::Action& action, const AZ::EntityComponentIdPair& idPair) { + // manipulator offsets do not take transform scale into account, need to handle it here + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + const AZ::Vector3 manipulatorPosition = AzToolsFramework::GetPositionInManipulatorFrame( + m_radiusManipulator->GetSpace().GetUniformScale(), colliderLocalTransform, action); + // Get the distance the manipulator has moved along the axis. - float extent = action.LocalPosition().Dot(action.m_fixed.m_axis); + float extent = manipulatorPosition.Dot(action.m_fixed.m_axis); // Clamp the distance to a small value to prevent it going negative. extent = AZ::GetMax(extent, MinSphereRadius); // Update the manipulator and sphere radius - m_radiusManipulator->SetLocalPosition(extent * action.m_fixed.m_axis); + m_radiusManipulator->SetLocalTransform( + Utils::GetColliderLocalTransform(idPair) * AZ::Transform::CreateTranslation(extent * action.m_fixed.m_axis)); PhysX::EditorColliderComponentRequestBus::Event(idPair, &PhysX::EditorColliderComponentRequests::SetSphereRadius, extent); } } diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index 69ae037e07..d432655e78 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp @@ -582,9 +582,8 @@ namespace PhysX AZ::Transform EditorColliderComponent::GetColliderLocalTransform() const { - const AZ::Vector3 nonUniformScale = Utils::GetTransformScale(GetEntityId()); return AZ::Transform::CreateFromQuaternionAndTranslation( - m_configuration.m_rotation, m_configuration.m_position * nonUniformScale); + m_configuration.m_rotation, m_configuration.m_position); } void EditorColliderComponent::UpdateMeshAsset() @@ -970,8 +969,10 @@ namespace PhysX static_cast(shapeConfiguration); const AZ::Vector3 overallScale = Utils::GetTransformScale(GetEntityId()) * m_cachedNonUniformScale * assetScale; + Physics::ColliderConfiguration nonUniformScaledColliderConfiguration = *colliderConfiguration; + nonUniformScaledColliderConfiguration.m_position *= m_cachedNonUniformScale; - m_colliderDebugDraw.DrawMesh(debugDisplay, *colliderConfiguration, *cookedMeshShapeConfiguration, + m_colliderDebugDraw.DrawMesh(debugDisplay, nonUniformScaledColliderConfiguration, *cookedMeshShapeConfiguration, overallScale, static_cast(shapeIndex)); break; } @@ -1054,12 +1055,17 @@ namespace PhysX AZ::Transform EditorColliderComponent::GetCurrentTransform() { - return GetColliderWorldTransform(); + return GetWorldTM(); + } + + AZ::Transform EditorColliderComponent::GetCurrentLocalTransform() + { + return GetColliderLocalTransform(); } AZ::Vector3 EditorColliderComponent::GetBoxScale() { - return AZ::Vector3(GetWorldTM().GetUniformScale()); + return AZ::Vector3::CreateOne(); } void EditorColliderComponent::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world) @@ -1203,7 +1209,7 @@ namespace PhysX AZ::Transform EditorColliderComponent::GetColliderWorldTransform() { - return AzToolsFramework::TransformNormalizedScale(GetWorldTM()) * GetColliderLocalTransform(); + return GetWorldTM() * GetColliderLocalTransform(); } bool EditorColliderComponent::ShouldUpdateCollisionMeshFromRender() const diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.h b/Gems/PhysX/Code/Source/EditorColliderComponent.h index 773dd0a9b8..bc3d4da6d9 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.h @@ -171,6 +171,7 @@ namespace PhysX AZ::Vector3 GetDimensions() override; void SetDimensions(const AZ::Vector3& dimensions) override; AZ::Transform GetCurrentTransform() override; + AZ::Transform GetCurrentLocalTransform() override; AZ::Vector3 GetBoxScale() override; // AZ::Render::MeshComponentNotificationBus diff --git a/Gems/PhysX/Code/Source/Utils.cpp b/Gems/PhysX/Code/Source/Utils.cpp index cc4ef2a12c..9848ab0415 100644 --- a/Gems/PhysX/Code/Source/Utils.cpp +++ b/Gems/PhysX/Code/Source/Utils.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -832,6 +833,17 @@ namespace PhysX return AZ::Transform::CreateFromQuaternionAndTranslation(colliderRelativeRotation, colliderRelativePosition); } + AZ::Transform GetColliderLocalTransform(const AZ::EntityComponentIdPair& idPair) + { + AZ::Quaternion colliderRotation = AZ::Quaternion::CreateIdentity(); + PhysX::EditorColliderComponentRequestBus::EventResult(colliderRotation, idPair, &PhysX::EditorColliderComponentRequests::GetColliderRotation); + + AZ::Vector3 colliderOffset = AZ::Vector3::CreateZero(); + PhysX::EditorColliderComponentRequestBus::EventResult(colliderOffset, idPair, &PhysX::EditorColliderComponentRequests::GetColliderOffset); + + return AZ::Transform::CreateFromQuaternionAndTranslation(colliderRotation, colliderOffset); + } + AZ::Transform GetColliderWorldTransform(const AZ::Transform& worldTransform, const AZ::Vector3& colliderRelativePosition, const AZ::Quaternion& colliderRelativeRotation) diff --git a/Gems/PhysX/Code/Source/Utils.h b/Gems/PhysX/Code/Source/Utils.h index 5244926aa2..f9e25a59f2 100644 --- a/Gems/PhysX/Code/Source/Utils.h +++ b/Gems/PhysX/Code/Source/Utils.h @@ -142,6 +142,9 @@ namespace PhysX AZ::Transform GetColliderLocalTransform(const AZ::Vector3& colliderRelativePosition , const AZ::Quaternion& colliderRelativeRotation); + //! Gets the local transform for a collider (the position and rotation relative to its entity). + AZ::Transform GetColliderLocalTransform(const AZ::EntityComponentIdPair& idPair); + //! Combines collider position and orientation offsets and world transform to a transform. AZ::Transform GetColliderWorldTransform(const AZ::Transform& worldTransform , const AZ::Vector3& colliderRelativePosition diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersBenchmarks.cpp b/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersBenchmarks.cpp index cd9e3c0395..55d7305a70 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersBenchmarks.cpp +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersBenchmarks.cpp @@ -235,7 +235,7 @@ namespace PhysX::Benchmarks //setup the frame timer tracker AZStd::vector tickTimes; tickTimes.reserve(CharacterConstants::GameFramesToSimulate); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (AZ::u32 i = 0; i < CharacterConstants::GameFramesToSimulate; i++) { @@ -294,7 +294,7 @@ namespace PhysX::Benchmarks AZStd::vector tickTimes; tickTimes.reserve(CharacterConstants::GameFramesToSimulate); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (AZ::u32 i = 0; i < CharacterConstants::GameFramesToSimulate; i++) { @@ -361,7 +361,7 @@ namespace PhysX::Benchmarks //setup the frame timer tracker AZStd::vector tickTimes; tickTimes.reserve(CharacterConstants::GameFramesToSimulate); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { //run each simulation part, and change direction each time for (AZ::u32 i = 0; i < numDirectionChanges; i++) diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersRagdollBenchmarks.cpp b/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersRagdollBenchmarks.cpp index c82d222e80..e86125c1f5 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersRagdollBenchmarks.cpp +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXCharactersRagdollBenchmarks.cpp @@ -198,7 +198,7 @@ namespace PhysX::Benchmarks //setup the frame timer tracker AZStd::vector tickTimes; tickTimes.reserve(RagdollConstants::GameFramesToSimulate); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (AZ::u32 i = 0; i < RagdollConstants::GameFramesToSimulate; i++) { @@ -264,7 +264,7 @@ namespace PhysX::Benchmarks //setup the frame timer tracker AZStd::vector tickTimes; tickTimes.reserve(RagdollConstants::GameFramesToSimulate); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (AZ::u32 i = 0; i < RagdollConstants::GameFramesToSimulate; i++) { diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXGenericBenchmarks.cpp b/Gems/PhysX/Code/Tests/Benchmarks/PhysXGenericBenchmarks.cpp index c2cf7f1246..1e556c3af7 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXGenericBenchmarks.cpp +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXGenericBenchmarks.cpp @@ -31,7 +31,7 @@ namespace PhysX::Benchmarks void BM_PhysXBenchmarkFixture(benchmark::State& state) { - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { auto fixture = std::make_unique(); fixture->SetUp(); diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXJointBenchmarks.cpp b/Gems/PhysX/Code/Tests/Benchmarks/PhysXJointBenchmarks.cpp index de8a9d2146..8606735174 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXJointBenchmarks.cpp +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXJointBenchmarks.cpp @@ -245,7 +245,7 @@ namespace PhysX::Benchmarks //setup the frame timer tracker Types::TimeList tickTimes; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (AZ::u32 i = 0; i < JointConstants::GameFramesToSimulate; i++) { @@ -300,7 +300,7 @@ namespace PhysX::Benchmarks //setup the frame timer tracker Types::TimeList tickTimes; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (AZ::u32 i = 0; i < JointConstants::GameFramesToSimulate; i++) { @@ -399,7 +399,7 @@ namespace PhysX::Benchmarks //setup the frame timer tracker Types::TimeList tickTimes; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (AZ::u32 i = 0; i < JointConstants::GameFramesToSimulate; i++) { diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXRigidBodyBenchmarks.cpp b/Gems/PhysX/Code/Tests/Benchmarks/PhysXRigidBodyBenchmarks.cpp index 58cbd0da23..616c248667 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXRigidBodyBenchmarks.cpp +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXRigidBodyBenchmarks.cpp @@ -229,7 +229,7 @@ namespace PhysX::Benchmarks //setup the frame timer tracker Types::TimeList tickTimes; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (AZ::u32 i = 0; i < RigidBodyConstants::GameFramesToSimulate; i++) { @@ -302,7 +302,7 @@ namespace PhysX::Benchmarks //setup the frame timer tracker AZStd::vector tickTimes; tickTimes.reserve(RigidBodyConstants::GameFramesToSimulate); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (AZ::u32 i = 0; i < RigidBodyConstants::GameFramesToSimulate; i++) { @@ -471,7 +471,7 @@ namespace PhysX::Benchmarks //setup the frame timer tracker AZStd::vector tickTimes; tickTimes.reserve(RigidBodyConstants::GameFramesToSimulate); - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { for (AZ::u32 i = 0; i < RigidBodyConstants::GameFramesToSimulate; i++) { diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXSceneQueryBenchmarks.cpp b/Gems/PhysX/Code/Tests/Benchmarks/PhysXSceneQueryBenchmarks.cpp index 9a8a28a85e..ba3f7aad40 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXSceneQueryBenchmarks.cpp +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXSceneQueryBenchmarks.cpp @@ -141,7 +141,7 @@ namespace PhysX::Benchmarks auto* sceneInterface = AZ::Interface::Get(); auto next = 0; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { request.m_direction = m_boxes[next].GetNormalized(); @@ -175,7 +175,7 @@ namespace PhysX::Benchmarks auto* sceneInterface = AZ::Interface::Get(); auto next = 0; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { request.m_direction = m_boxes[next].GetNormalized(); @@ -206,7 +206,7 @@ namespace PhysX::Benchmarks auto* sceneInterface = AZ::Interface::Get(); auto next = 0; - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { request.m_pose = AZ::Transform::CreateTranslation(m_boxes[next]); diff --git a/Gems/PhysX/Code/Tests/PhysXColliderComponentModeTests.cpp b/Gems/PhysX/Code/Tests/PhysXColliderComponentModeTests.cpp index 5722d1a397..a9797d3d53 100644 --- a/Gems/PhysX/Code/Tests/PhysXColliderComponentModeTests.cpp +++ b/Gems/PhysX/Code/Tests/PhysXColliderComponentModeTests.cpp @@ -17,7 +17,9 @@ #include #include #include +#include #include +#include namespace UnitTest { @@ -437,4 +439,378 @@ namespace UnitTest EXPECT_NEAR(assetScale.GetY(), 1.0f, tolerance); EXPECT_NEAR(assetScale.GetZ(), 1.0f, tolerance); } + + class PhysXEditorColliderComponentFixture : public UnitTest::ToolsApplicationFixture + { + public: + void SetUpEditorFixtureImpl() override; + void TearDownEditorFixtureImpl() override; + void SetupTransform(const AZ::Quaternion& rotation, const AZ::Vector3& translation, float uniformScale); + void SetupCollider( + const Physics::ShapeConfiguration& shapeConfiguration, + const AZ::Quaternion& colliderRotation, + const AZ::Vector3& colliderOffset); + void SetupNonUniformScale(const AZ::Vector3& nonUniformScale); + void EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode subMode); + + AZ::Entity* m_entity = nullptr; + AZ::EntityComponentIdPair m_idPair; + }; + + void PhysXEditorColliderComponentFixture::SetUpEditorFixtureImpl() + { + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + + UnitTest::CreateDefaultEditorEntity("EditorColliderComponentEntity", &m_entity); + } + + void PhysXEditorColliderComponentFixture::TearDownEditorFixtureImpl() + { + AzToolsFramework::EditorEntityContextRequestBus::Broadcast( + &AzToolsFramework::EditorEntityContextRequestBus::Events::DestroyEditorEntity, m_entity->GetId()); + m_entity = nullptr; + } + + void PhysXEditorColliderComponentFixture::SetupTransform( + const AZ::Quaternion& rotation, const AZ::Vector3& translation, float uniformScale) + { + const AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(rotation, translation); + AZ::TransformBus::Event(m_entity->GetId(), &AZ::TransformBus::Events::SetWorldTM, transform); + AZ::TransformBus::Event(m_entity->GetId(), &AZ::TransformBus::Events::SetLocalUniformScale, uniformScale); + } + + void PhysXEditorColliderComponentFixture::SetupCollider( + const Physics::ShapeConfiguration& shapeConfiguration, const AZ::Quaternion& colliderRotation, const AZ::Vector3& colliderOffset) + { + m_entity->Deactivate(); + auto* colliderComponent = + m_entity->CreateComponent(Physics::ColliderConfiguration(), shapeConfiguration); + m_entity->Activate(); + m_idPair = AZ::EntityComponentIdPair(m_entity->GetId(), colliderComponent->GetId()); + PhysX::EditorColliderComponentRequestBus::Event( + m_idPair, &PhysX::EditorColliderComponentRequests::SetColliderOffset, colliderOffset); + PhysX::EditorColliderComponentRequestBus::Event( + m_idPair, &PhysX::EditorColliderComponentRequests::SetColliderRotation, colliderRotation); + } + + void PhysXEditorColliderComponentFixture::SetupNonUniformScale(const AZ::Vector3& nonUniformScale) + { + m_entity->Deactivate(); + m_entity->CreateComponent(AzToolsFramework::Components::EditorNonUniformScaleComponent::RTTI_Type()); + m_entity->Activate(); + AZ::NonUniformScaleRequestBus::Event(m_entity->GetId(), &AZ::NonUniformScaleRequests::SetScale, nonUniformScale); + } + + void PhysXEditorColliderComponentFixture::EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode subMode) + { + AzToolsFramework::SelectEntity(m_entity->GetId()); + EnterComponentMode(); + PhysX::ColliderComponentModeRequestBus::Broadcast(&PhysX::ColliderComponentModeRequests::SetCurrentMode, subMode); + } + + using PhysXEditorColliderComponentManipulatorFixture = + UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin; + + // use a reasonably large tolerance because manipulator precision is limited by viewport resolution + static const float ManipulatorTolerance = 0.01f; + + TEST_F(PhysXEditorColliderComponentManipulatorFixture, OffsetManipulatorsCorrectlyLocatedRelativeToCollider) + { + const AZ::Vector3 boxDimensions(2.0f, 3.0f, 1.5f); + const AZ::Quaternion boxRotation(0.1f, 0.1f, 0.7f, 0.7f); + const AZ::Vector3 boxOffset(3.0f, 1.0f, 2.0f); + SetupCollider(Physics::BoxShapeConfiguration(boxDimensions), boxRotation, boxOffset); + const AZ::Quaternion entityRotation(0.8f, 0.2f, 0.4f, 0.4f); + const AZ::Vector3 entityTranslation(2.0f, -3.0f, 0.5f); + const float uniformScale = 2.0f; + SetupTransform(entityRotation, entityTranslation, uniformScale); + EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode::Offset); + + // the expected position of the collider centre based on the combination of entity transform and collider offset + const AZ::Vector3 expectedColliderPosition(8.8f, -2.28f, 3.54f); + + // the expected world space direction of the collider offset x-axis based on the entity transform + const AZ::Vector3 expectedXAxis(0.6f, 0.64f, 0.48f); + + // position the camera to look down at the collider from above + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(-AZ::Constants::HalfPi), expectedColliderPosition + AZ::Vector3::CreateAxisZ(10.0f))); + + // position in world space, slightly moved along the x-axis in order to grab the x translation manipulator + const AZ::Vector3 worldStart = expectedColliderPosition + 0.5f * expectedXAxis; + + // position in world space to move to + const AZ::Vector3 worldEnd = worldStart + 2.0f * expectedXAxis; + + const auto screenStart = AzFramework::WorldToScreen(worldStart, m_cameraState); + const auto screenEnd = AzFramework::WorldToScreen(worldEnd, m_cameraState); + + m_actionDispatcher + ->CameraState(m_cameraState) + // move the mouse to the position of the x offset manipulator + ->MousePosition(screenStart) + // drag to move the manipulator + ->MouseLButtonDown() + ->MousePosition(screenEnd) + ->MouseLButtonUp(); + + AZ::Vector3 newColliderOffset = AZ::Vector3::CreateZero(); + PhysX::EditorColliderComponentRequestBus::EventResult( + newColliderOffset, m_idPair, &PhysX::EditorColliderComponentRequests::GetColliderOffset); + + EXPECT_THAT(newColliderOffset, IsCloseTolerance(AZ::Vector3(4.0f, 1.0f, 2.0f), ManipulatorTolerance)); + } + + TEST_F(PhysXEditorColliderComponentManipulatorFixture, OffsetManipulatorsCorrectlyLocatedRelativeToColliderWithNonUniformScale) + { + const float capsuleRadius = 0.5f; + const float capsuleHeight = 2.0f; + const AZ::Quaternion capsuleRotation(0.2f, -0.4f, 0.8f, 0.4f); + const AZ::Vector3 capsuleOffset(-2.0f, 3.0f, -1.0f); + SetupCollider(Physics::CapsuleShapeConfiguration(capsuleHeight, capsuleRadius), capsuleRotation, capsuleOffset); + const AZ::Quaternion entityRotation(-0.1f, 0.7f, -0.7f, 0.1f); + const AZ::Vector3 entityTranslation(-1.0f, 1.0f, -2.5f); + const float uniformScale = 1.5f; + SetupTransform(entityRotation, entityTranslation, uniformScale); + const AZ::Vector3 nonUniformScale(2.0f, 0.5f, 1.5f); + SetupNonUniformScale(nonUniformScale); + EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode::Offset); + + // the expected position of the collider centre based on the combination of entity transform, collider offset and non-uniform scale + const AZ::Vector3 expectedColliderPosition(4.13f, 4.84f, -4.75f); + + // the expected world space direction of the collider offset z-axis based on the entity transform + const AZ::Vector3 expectedZAxis(0.28f, -0.96f, 0.0f); + + // position the camera to look at the collider from underneath + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::Constants::HalfPi), expectedColliderPosition - AZ::Vector3::CreateAxisZ(10.0f))); + + // position in world space, slightly moved along the z-axis in order to grab the z translation manipulator + // need to go in the negative z direction because the camera angle causes the manipulator to flip + const AZ::Vector3 worldStart = expectedColliderPosition - 0.5f * expectedZAxis; + + // position in world space to move to + const AZ::Vector3 worldEnd = worldStart - 2.25f * expectedZAxis; + + const auto screenStart = AzFramework::WorldToScreen(worldStart, m_cameraState); + const auto screenEnd = AzFramework::WorldToScreen(worldEnd, m_cameraState); + + m_actionDispatcher + ->CameraState(m_cameraState) + // move the mouse to the position of the z offset manipulator + ->MousePosition(screenStart) + // drag to move the manipulator + ->MouseLButtonDown() + ->MousePosition(screenEnd) + ->MouseLButtonUp(); + + AZ::Vector3 newColliderOffset = AZ::Vector3::CreateZero(); + PhysX::EditorColliderComponentRequestBus::EventResult( + newColliderOffset, m_idPair, &PhysX::EditorColliderComponentRequests::GetColliderOffset); + + EXPECT_THAT(newColliderOffset, IsCloseTolerance(AZ::Vector3(-2.0f, 3.0f, -2.0f), ManipulatorTolerance)); + } + + TEST_F(PhysXEditorColliderComponentManipulatorFixture, BoxColliderScaleManipulatorsCorrectlyLocatedRelativeToColliderWithNonUniformScale) + { + const AZ::Vector3 boxDimensions(2.0f, 2.0f, 3.0f); + const AZ::Quaternion boxRotation(0.7f, 0.7f, -0.1f, 0.1f); + const AZ::Vector3 boxOffset(0.5f, 1.5f, 2.0f); + SetupCollider(Physics::BoxShapeConfiguration(boxDimensions), boxRotation, boxOffset); + const AZ::Quaternion entityRotation(0.2f, 0.4f, -0.4f, 0.8f); + const AZ::Vector3 entityTranslation(2.0f, -3.0f, -2.0f); + const float uniformScale = 0.5f; + SetupTransform(entityRotation, entityTranslation, uniformScale); + const AZ::Vector3 nonUniformScale(3.0f, 1.5f, 2.5f); + SetupNonUniformScale(nonUniformScale); + EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode::Dimensions); + + // the expected position of the collider centre based on the combination of entity transform, collider offset and non-uniform scale + const AZ::Vector3 expectedColliderPosition(4.37f, -4.285f, -1.1f); + + // the expected position of the y scale manipulator relative to the centre of the collider, based on collider + // rotation, entity rotation and scale, and non-uniform scale + const AZ::Vector3 scaleManipulatorYDelta(0.54f, -0.72f, -1.2f); + + // position the camera to look at the collider along the x-y diagonal + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationZ(-AZ::Constants::QuarterPi), expectedColliderPosition - AZ::Vector3(2.0f, 2.0f, 0.0f))); + + const AZ::Vector3 worldStart = expectedColliderPosition + scaleManipulatorYDelta; + const AZ::Vector3 worldEnd = worldStart + 0.1f * scaleManipulatorYDelta; + + const auto screenStart = AzFramework::WorldToScreen(worldStart, m_cameraState); + const auto screenEnd = AzFramework::WorldToScreen(worldEnd, m_cameraState); + + m_actionDispatcher + ->CameraState(m_cameraState) + // move the mouse to the position of the y scale manipulator + ->MousePosition(screenStart) + // drag to move the manipulator + ->MouseLButtonDown() + ->MousePosition(screenEnd) + ->MouseLButtonUp(); + + AZ::Vector3 newBoxDimensions = AZ::Vector3::CreateZero(); + AzToolsFramework::BoxManipulatorRequestBus::EventResult( + newBoxDimensions, m_idPair, &AzToolsFramework::BoxManipulatorRequests::GetDimensions); + + EXPECT_THAT(newBoxDimensions, IsCloseTolerance(AZ::Vector3(2.0f, 2.2f, 3.0f), ManipulatorTolerance)); + } + + TEST_F(PhysXEditorColliderComponentManipulatorFixture, SphereColliderScaleManipulatorsCorrectlyLocatedRelativeToColliderWithNonUniformScale) + { + const float sphereRadius = 1.0f; + const AZ::Quaternion sphereRotation(-0.1f, 0.7f, -0.7f, 0.1f); + const AZ::Vector3 sphereOffset(-2.0f, 1.0f, -3.0f); + SetupCollider(Physics::SphereShapeConfiguration(sphereRadius), sphereRotation, sphereOffset); + const AZ::Quaternion entityRotation(-0.4f, -0.2f, 0.4f, 0.8f); + const AZ::Vector3 entityTranslation(-1.0f, -3.0f, 3.0f); + const float uniformScale = 1.5f; + SetupTransform(entityRotation, entityTranslation, uniformScale); + const AZ::Vector3 nonUniformScale(1.5f, 0.5f, 2.0f); + SetupNonUniformScale(nonUniformScale); + EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode::Dimensions); + + // the expected position of the collider centre based on the combination of entity transform, collider offset and non-uniform scale + const AZ::Vector3 expectedColliderPosition(1.7f, -10.65f, -3.0f); + + // position the camera to look at the collider along the y-axis + AzFramework::SetCameraTransform( + m_cameraState, AZ::Transform::CreateTranslation(expectedColliderPosition - AZ::Vector3(0.0f, 5.0f, 0.0f))); + + // the expected position of the scale manipulator relative to the centre of the collider, based on collider + // rotation, entity scale, non-uniform scale and camera state + const AZ::Vector3 scaleManipulatorDelta(-1.1952f, -1.8036f, 0.168f); + + const AZ::Vector3 worldStart = expectedColliderPosition + scaleManipulatorDelta; + const AZ::Vector3 worldEnd = worldStart - 0.1f * scaleManipulatorDelta; + + const auto screenStart = AzFramework::WorldToScreen(worldStart, m_cameraState); + const auto screenEnd = AzFramework::WorldToScreen(worldEnd, m_cameraState); + + m_actionDispatcher + ->CameraState(m_cameraState) + // move the mouse to the position of the y scale manipulator + ->MousePosition(screenStart) + // drag to move the manipulator + ->MouseLButtonDown() + ->MousePosition(screenEnd) + ->MouseLButtonUp(); + + float newSphereRadius = 0.0f; + PhysX::EditorColliderComponentRequestBus::EventResult( + newSphereRadius, m_idPair, &PhysX::EditorColliderComponentRequests::GetSphereRadius); + + EXPECT_NEAR(newSphereRadius, 0.9f, ManipulatorTolerance); + } + + TEST_F(PhysXEditorColliderComponentManipulatorFixture, CapsuleColliderScaleManipulatorsCorrectlyLocatedRelativeToColliderWithNonUniformScale) + { + const float capsuleRadius = 0.2f; + const float capsuleHeight = 1.0f; + const AZ::Quaternion capsuleRotation(-0.2f, -0.8f, -0.4f, 0.4f); + const AZ::Vector3 capsuleOffset(1.0f, -2.0f, 1.0f); + SetupCollider(Physics::CapsuleShapeConfiguration(capsuleHeight, capsuleRadius), capsuleRotation, capsuleOffset); + const AZ::Quaternion entityRotation(0.7f, -0.1f, -0.1f, 0.7f); + const AZ::Vector3 entityTranslation(-2.0f, 1.0f, -3.0f); + const float uniformScale = 2.0f; + SetupTransform(entityRotation, entityTranslation, uniformScale); + const AZ::Vector3 nonUniformScale(1.0f, 0.5f, 1.5f); + SetupNonUniformScale(nonUniformScale); + EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode::Dimensions); + + // the expected position of the collider centre based on the combination of entity transform, collider offset and non-uniform scale + const AZ::Vector3 expectedColliderPosition(-0.92f, -2.44f, -5.0f); + + // the expected position of the height manipulator relative to the centre of the collider, based on collider + // rotation, entity scale and non-uniform scale + const AZ::Vector3 heightManipulatorDelta(-0.3096f, 0.6528f, 0.4f); + + // position the camera to look at the collider along the y-z diagonal + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(-AZ::Constants::QuarterPi), expectedColliderPosition + AZ::Vector3(0.0f, -1.0f, 1.0f))); + + const AZ::Vector3 worldStart = expectedColliderPosition + heightManipulatorDelta; + const AZ::Vector3 worldEnd = worldStart + 0.2f * heightManipulatorDelta; + + const auto screenStart = AzFramework::WorldToScreen(worldStart, m_cameraState); + const auto screenEnd = AzFramework::WorldToScreen(worldEnd, m_cameraState); + + m_actionDispatcher + ->CameraState(m_cameraState) + // move the mouse to the position of the height manipulator + ->MousePosition(screenStart) + // drag to move the manipulator + ->MouseLButtonDown() + ->MousePosition(screenEnd) + ->MouseLButtonUp(); + + float newCapsuleHeight = 0.0f; + PhysX::EditorColliderComponentRequestBus::EventResult( + newCapsuleHeight, m_idPair, &PhysX::EditorColliderComponentRequests::GetCapsuleHeight); + + EXPECT_NEAR(newCapsuleHeight, 1.2f, ManipulatorTolerance); + } + + TEST_F(PhysXEditorColliderComponentManipulatorFixture, ColliderRotationManipulatorsCorrectlyLocatedRelativeToColliderWithNonUniformScale) + { + const float capsuleRadius = 1.2f; + const float capsuleHeight = 4.0f; + const AZ::Quaternion capsuleRotation(0.7f, 0.7f, -0.1f, 0.1f); + const AZ::Vector3 capsuleOffset(-2.0f, -2.0f, 1.0f); + SetupCollider(Physics::CapsuleShapeConfiguration(capsuleHeight, capsuleRadius), capsuleRotation, capsuleOffset); + const AZ::Quaternion entityRotation(0.8f, -0.4f, -0.4f, 0.2f); + const AZ::Vector3 entityTranslation(1.0f, -1.5f, 2.0f); + const float uniformScale = 1.5f; + SetupTransform(entityRotation, entityTranslation, uniformScale); + const AZ::Vector3 nonUniformScale(1.5f, 1.5f, 2.0f); + SetupNonUniformScale(nonUniformScale); + EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode::Rotation); + + // the expected position of the collider centre based on the combination of entity transform, collider offset and non-uniform scale + const AZ::Vector3 expectedColliderPosition(-0.86f, 4.8f, -0.52f); + + // the y and z axes of the collider's frame in world space, used to locate points on the x rotation manipulator arc to interact with + const AZ::Vector3 yDirection(0.36f, -0.8f, -0.48f); + const AZ::Vector3 zDirection(0.9024f, 0.168f, 0.3968f); + + // position the camera to look at the collider along the world y axis + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateTranslation(expectedColliderPosition - AZ::Vector3(0.0f, 10.0f, 0.0f))); + + const float screenToWorldMultiplier = AzToolsFramework::CalculateScreenToWorldMultiplier(expectedColliderPosition, m_cameraState); + const float manipulatorViewRadius = 2.0f; + const AZ::Vector3 worldStart = expectedColliderPosition + screenToWorldMultiplier * manipulatorViewRadius * yDirection; + const AZ::Vector3 worldEnd = expectedColliderPosition + screenToWorldMultiplier * manipulatorViewRadius * zDirection; + + const auto screenStart = AzFramework::WorldToScreen(worldStart, m_cameraState); + const auto screenEnd = AzFramework::WorldToScreen(worldEnd, m_cameraState); + + m_actionDispatcher + ->CameraState(m_cameraState) + // move the mouse to a position on the angular manipulator + ->MousePosition(screenStart) + // drag to move the manipulator + ->MouseLButtonDown() + ->MousePosition(screenEnd) + ->MouseLButtonUp(); + + AZ::Quaternion newColliderRotation = AZ::Quaternion::CreateIdentity(); + PhysX::EditorColliderComponentRequestBus::EventResult( + newColliderRotation, m_idPair, &PhysX::EditorColliderComponentRequests::GetColliderRotation); + + EXPECT_THAT(newColliderRotation, testing::Not(IsCloseTolerance(capsuleRotation, ManipulatorTolerance))); + } } // namespace UnitTest diff --git a/Gems/Prefab/PrefabBuilder/PrefabBuilderComponent.cpp b/Gems/Prefab/PrefabBuilder/PrefabBuilderComponent.cpp index 71464501ba..a79f9e4cdb 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabBuilderComponent.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabBuilderComponent.cpp @@ -76,7 +76,7 @@ namespace AZ::Prefab // Deserialize all of the entities and their components (for this prefab only) auto newInstance = AZStd::make_unique(); - AzToolsFramework::Prefab::Instance::EntityList entities; + AzToolsFramework::EntityList entities; if (AzToolsFramework::Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(*newInstance, entities, genericDocument)) { // Add the fingerprint of all the components and their types diff --git a/Gems/SurfaceData/Code/Include/SurfaceData/Components/SurfaceDataColliderComponent.h b/Gems/SurfaceData/Code/Include/SurfaceData/Components/SurfaceDataColliderComponent.h index 2ef25774aa..1ff8ab6f4f 100644 --- a/Gems/SurfaceData/Code/Include/SurfaceData/Components/SurfaceDataColliderComponent.h +++ b/Gems/SurfaceData/Code/Include/SurfaceData/Components/SurfaceDataColliderComponent.h @@ -82,6 +82,7 @@ namespace SurfaceData //////////////////////////////////////////////////////////////////////// // SurfaceDataProviderRequestBus void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const override; + void GetSurfacePointsFromList(AZStd::span inPositions, SurfacePointList& surfacePointList) const override; ////////////////////////////////////////////////////////////////////////// // SurfaceDataModifierRequestBus diff --git a/Gems/SurfaceData/Code/Include/SurfaceData/Components/SurfaceDataShapeComponent.h b/Gems/SurfaceData/Code/Include/SurfaceData/Components/SurfaceDataShapeComponent.h index 25ac491809..f43ae90574 100644 --- a/Gems/SurfaceData/Code/Include/SurfaceData/Components/SurfaceDataShapeComponent.h +++ b/Gems/SurfaceData/Code/Include/SurfaceData/Components/SurfaceDataShapeComponent.h @@ -66,6 +66,7 @@ namespace SurfaceData ////////////////////////////////////////////////////////////////////////// // SurfaceDataProviderRequestBus void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const override; + void GetSurfacePointsFromList(AZStd::span inPositions, SurfacePointList& surfacePointList) const override; ////////////////////////////////////////////////////////////////////////// // SurfaceDataModifierRequestBus diff --git a/Gems/SurfaceData/Code/Include/SurfaceData/Components/SurfaceDataSystemComponent.h b/Gems/SurfaceData/Code/Include/SurfaceData/Components/SurfaceDataSystemComponent.h index 3e1291e524..820a1296c5 100644 --- a/Gems/SurfaceData/Code/Include/SurfaceData/Components/SurfaceDataSystemComponent.h +++ b/Gems/SurfaceData/Code/Include/SurfaceData/Components/SurfaceDataSystemComponent.h @@ -42,11 +42,17 @@ namespace SurfaceData void GetSurfacePoints(const AZ::Vector3& inPosition, const SurfaceTagVector& desiredTags, SurfacePointList& surfacePointList) const override; void GetSurfacePointsFromRegion( const AZ::Aabb& inRegion, const AZ::Vector2 stepSize, const SurfaceTagVector& desiredTags, - SurfacePointLists& surfacePointListPerPosition) const override; + SurfacePointList& surfacePointListPerPosition) const override; void GetSurfacePointsFromList( AZStd::span inPositions, const SurfaceTagVector& desiredTags, - SurfacePointLists& surfacePointLists) const override; + SurfacePointList& surfacePointLists) const override; + + void GetSurfacePointsFromListInternal( + AZStd::span inPositions, + const AZ::Aabb& inBounds, + const SurfaceTagVector& desiredTags, + SurfacePointList& surfacePointLists) const; SurfaceDataRegistryHandle RegisterSurfaceDataProvider(const SurfaceDataRegistryEntry& entry) override; void UnregisterSurfaceDataProvider(const SurfaceDataRegistryHandle& handle) override; diff --git a/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataModifierRequestBus.h b/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataModifierRequestBus.h index 19cc84fae2..6fc6766f5c 100644 --- a/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataModifierRequestBus.h +++ b/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataModifierRequestBus.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace SurfaceData { diff --git a/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataProviderRequestBus.h b/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataProviderRequestBus.h index b3c8667679..3d42f6061b 100644 --- a/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataProviderRequestBus.h +++ b/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataProviderRequestBus.h @@ -9,7 +9,9 @@ #pragma once #include +#include #include +#include namespace SurfaceData { @@ -32,9 +34,19 @@ namespace SurfaceData //! Get all of the surface points that this provider has at the given input position. //! @param inPosition - The input position to query. Only XY are guaranteed to be valid, Z should be ignored. - //! @param surfacePointList - The output list of surface points generated, if any. Each provider is expected to - //! append to this list, not overwrite it. + //! @param surfacePointList - The input/output SurfacePointList to add any generated surface points to. virtual void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const = 0; + + //! Get all of the surface points that this provider has at the given input positions. + //! @param inPositions - The input positions to query. Only XY are guaranteed to be valid, Z should be ignored. + //! @param surfacePointList - The input/output SurfacePointList to add any generated surface points to. + virtual void GetSurfacePointsFromList(AZStd::span inPositions, SurfacePointList& surfacePointList) const + { + for (auto& inPosition : inPositions) + { + GetSurfacePoints(inPosition, surfacePointList); + } + } }; typedef AZ::EBus SurfaceDataProviderRequestBus; diff --git a/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataSystemRequestBus.h b/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataSystemRequestBus.h index d4e84c5974..c3309707b6 100644 --- a/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataSystemRequestBus.h +++ b/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataSystemRequestBus.h @@ -14,6 +14,7 @@ #include #include #include +#include namespace SurfaceData { @@ -40,13 +41,13 @@ namespace SurfaceData // The input positions are chosen by starting at the min sides of inRegion and incrementing by stepSize. This method is inclusive // on the min sides of the AABB, and exclusive on the max sides (i.e. for a box of (0,0) - (4,4), the point (0,0) is included but (4,4) isn't). virtual void GetSurfacePointsFromRegion(const AZ::Aabb& inRegion, const AZ::Vector2 stepSize, const SurfaceTagVector& desiredTags, - SurfacePointLists& surfacePointLists) const = 0; + SurfacePointList& surfacePointLists) const = 0; // Get all surface points for every passed-in input position. Only the XY dimensions of each position are used. virtual void GetSurfacePointsFromList( AZStd::span inPositions, const SurfaceTagVector& desiredTags, - SurfacePointLists& surfacePointLists) const = 0; + SurfacePointList& surfacePointLists) const = 0; virtual SurfaceDataRegistryHandle RegisterSurfaceDataProvider(const SurfaceDataRegistryEntry& entry) = 0; virtual void UnregisterSurfaceDataProvider(const SurfaceDataRegistryHandle& handle) = 0; diff --git a/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataTypes.h b/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataTypes.h index 1d49fff3dc..95f64bcae3 100644 --- a/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataTypes.h +++ b/Gems/SurfaceData/Code/Include/SurfaceData/SurfaceDataTypes.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -166,7 +167,7 @@ namespace SurfaceData //! Check to see if the collection contains any of the given tags. //! @param sampleTags - The tags to look for. //! @return True if any of the tags is found, false if none are found. - bool HasAnyMatchingTags(const SurfaceTagVector& sampleTags) const; + bool HasAnyMatchingTags(AZStd::span sampleTags) const; //! Check to see if the collection contains the given tag with the given weight range. //! The range check is inclusive on both sides of the range: [weightMin, weightMax] @@ -174,7 +175,7 @@ namespace SurfaceData //! @param weightMin - The minimum weight for this tag. //! @param weightMax - The maximum weight for this tag. //! @return True if any of the tags is found, false if none are found. - bool HasAnyMatchingTags(const SurfaceTagVector& sampleTags, float weightMin, float weightMax) const; + bool HasAnyMatchingTags(AZStd::span sampleTags, float weightMin, float weightMax) const; private: //! Search for the given tag entry. @@ -185,79 +186,21 @@ namespace SurfaceData AZStd::fixed_vector m_weights; }; - //! SurfacePointList stores a collection of surface point data, which consists of positions, normals, and surface tag weights. - class SurfacePointList - { - public: - AZ_CLASS_ALLOCATOR(SurfacePointList, AZ::SystemAllocator, 0); - AZ_TYPE_INFO(SurfacePointList, "{DBA02848-2131-4279-BDEF-3581B76AB736}"); - - SurfacePointList() = default; - ~SurfacePointList() = default; - - //! Constructor for creating a SurfacePointList from a list of SurfacePoint data. - //! Primarily used as a convenience for unit tests. - //! @param surfacePoints - An initial set of SurfacePoint points to store in the SurfacePointList. - SurfacePointList(AZStd::initializer_list surfacePoints); - - //! Add a surface point to the list. - //! @param entityId - The entity creating the surface point. - //! @param position - The position of the surface point. - //! @param normal - The normal for the surface point. - //! @param weights - The surface tags and weights for this surface point. - void AddSurfacePoint(const AZ::EntityId& entityId, - const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceTagWeights& weights); - - //! Clear the surface point list. - void Clear(); - - //! Preallocate space in the list based on the maximum number of output points per input point we can generate. - //! @param maxPointsPerInput - The maximum number of output points per input point. - void ReserveSpace(size_t maxPointsPerInput); - - //! Check if the surface point list is empty. - //! @return - true if empty, false if it contains points. - bool IsEmpty() const; - - //! Get the size of the surface point list. - //! @return - The number of valid points in the list. - size_t GetSize() const; - - //! Enumerate every surface point and call a callback for each point found. - void EnumeratePoints(AZStd::function< - bool(const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceTagWeights& surfaceWeights)> pointCallback) const; - - //! Modify the surface weights for each surface point in the list. - void ModifySurfaceWeights( - const AZ::EntityId& currentEntityId, - AZStd::function modificationWeightCallback); - - //! Get the surface point with the highest Z value. - AzFramework::SurfaceData::SurfacePoint GetHighestSurfacePoint() const; - - //! Remove any points that don't contain any of the provided surface tags. - void FilterPoints(const SurfaceTagVector& desiredTags); - - protected: - // These are kept in separate parallel vectors instead of a single struct so that it's possible to pass just specific data - // "channels" into other methods as span<> without having to pass the full struct into the span<>. Specifically, we want to be - // able to pass spans of the positions down through nesting gradient/surface calls. - // A side benefit is that profiling showed the data access to be faster than packing all the fields into a single struct. - AZStd::vector m_surfaceCreatorIdList; - AZStd::vector m_surfacePositionList; - AZStd::vector m_surfaceNormalList; - AZStd::vector m_surfaceWeightsList; - - AZ::Aabb m_pointBounds = AZ::Aabb::CreateNull(); - }; - - using SurfacePointLists = AZStd::vector; struct SurfaceDataRegistryEntry { + //! The entity ID of the surface provider / modifier AZ::EntityId m_entityId; + + //! The AABB bounds that this surface provider / modifier can affect, or null if it has infinite bounds. AZ::Aabb m_bounds = AZ::Aabb::CreateNull(); + + //! The set of surface tags that this surface provider / modifier can create or add to a point. SurfaceTagVector m_tags; + + //! The maximum number of surface points that this will create per input position. + //! For surface modifiers, this is always expected to be 0, and for surface providers it's expected to be > 0. + size_t m_maxPointsCreatedPerInput = 0; }; using SurfaceDataRegistryHandle = AZ::u32; diff --git a/Gems/SurfaceData/Code/Include/SurfaceData/SurfacePointList.h b/Gems/SurfaceData/Code/Include/SurfaceData/SurfacePointList.h new file mode 100644 index 0000000000..83fb02c30a --- /dev/null +++ b/Gems/SurfaceData/Code/Include/SurfaceData/SurfacePointList.h @@ -0,0 +1,229 @@ +/* + * 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 + +namespace SurfaceData +{ + //! SurfacePointList stores a collection of surface point data, which consists of positions, normals, and surface tag weights. + //! This class is specifically designed to be used in the following ways. + //! + //! List construction: + //! StartListConstruction() - This clears the structure, temporarily holds on to the list of input positions, and preallocates the data. + //! AddSurfacePoint() - Add surface points to the list. They're expected to get added in input position order. + //! ModifySurfaceWeights() - Modify the surface weights for the set of input points. + //! FilterPoints() - Remove any generated surface points that don't fit the filter criteria + //! EndListConstruction() - "Freeze" and compact the data. + //! + //! List usage: + //! Any of the query APIs can be used in any order after the list has finished being constructed. + //! + //! This class is specifically designed around the usage patterns described above to minimize the amount of allocations and data + //! shifting that needs to occur. There are some tricky bits that need to be accounted for: + //! * Tracking which input positions each output point belongs to. + //! * Support for merging similar surface points together, which causes us to keep them sorted for easier comparisons. + //! * Each surface provider will add points in input position order, but we call each provider separately, so the added points will + //! show up like (0, 1, 2, 3), (0, 1, 3), (0, 0, 1, 2, 3), etc. We don't want to call each surface provider per-point, because that + //! incurs a lot of avoidable overhead in each provider. + //! * Output points get optionally filtered out at the very end if they don't match any of the filter tags passed in. + //! + //! The solution is that we always add new surface point data to the end of their respective vectors, but we also keep a helper + //! structure that's a list of lists of sorted indices. We can incrementally re-sort the indices quickly without having to shift + //! all the surface point data around. + class SurfacePointList + { + public: + AZ_CLASS_ALLOCATOR(SurfacePointList, AZ::SystemAllocator, 0); + AZ_TYPE_INFO(SurfacePointList, "{DBA02848-2131-4279-BDEF-3581B76AB736}"); + + SurfacePointList() = default; + ~SurfacePointList() = default; + + // ---------- List Construction APIs ------------- + + //! Clear the surface point list. + void Clear(); + + //! Constructor for creating a SurfacePointList from a list of SurfacePoint data. + //! Primarily used as a convenience for unit tests. + //! @param surfacePoints - A set of SurfacePoint points to store in the SurfacePointList. + //! Each point that's passed in will be treated as both the input and output position. + //! The list will be fully constructed and queryable after this runs. + SurfacePointList(AZStd::span surfacePoints); + + //! Start construction of a SurfacePointList from a list of SurfacePoint data. + //! Primarily used as a convenience for unit tests. + //! @param surfacePoints - A set of SurfacePoint points to store in the SurfacePointList. + //! The list will remain in the "constructing" state after this is called, so it will still be possible to add/modify + //! points, and EndListConstruction() will still need to be called. + void StartListConstruction(AZStd::span surfacePoints); + + //! Start construction of a SurfacePointList. + //! @param inPositions - the list of input positions that will be used to generate this list. This list is expected to remain + //! valid until EndListConstruction() is called. + //! @param maxPointsPerInput - the maximum number of potential surface points that will be generated for each input. This is used + //! for allocating internal structures during list construction and is enforced to be correct. + //! @param filterTags - optional list of tags to filter the generated surface points by. If this list is provided, every surface + //! point remaining in the list after construction will contain at least one of these tags. If the list is empty, all generated + //! points will remain in the list. The filterTags list is expected to remain valid until EndListConstruction() is called. + void StartListConstruction(AZStd::span inPositions, size_t maxPointsPerInput, + AZStd::span filterTags); + + //! Add a surface point to the list. + //! To use this method optimally, the points should get added in increasing inPosition index order. + //! @param entityId - The entity creating the surface point. + //! @param inPosition - The input position that produced this surface point. + //! @param position - The position of the surface point. + //! @param normal - The normal for the surface point. + //! @param weights - The surface tags and weights for this surface point. + void AddSurfacePoint(const AZ::EntityId& entityId, const AZ::Vector3& inPosition, + const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceTagWeights& weights); + + //! Modify the surface weights for each surface point in the list. + //! @param currentEntityId - The entity currently modifying the surface weights. This is used to prevent entities from modifying + //! any points they created. + //! @param modificationWeightCallback - The function to call for each surface point to process. + void ModifySurfaceWeights( + const AZ::EntityId& currentEntityId, + AZStd::function modificationWeightCallback); + + //! End construction of the SurfacePointList. + //! After this is called, surface points can no longer be added or modified, and all of the query APIs can start getting used. + void EndListConstruction(); + + // ---------- List Query APIs ------------- + + //! Return whether or not the entire surface point list is empty. + //! @return True if empty, false if it contains any points. + bool IsEmpty() const; + + //! Return whether or not a given input position index has any output points associated with it. + //! @param inputPositionIndex - The input position to look for output points for. + //! @return True if empty, false if there is at least one output point associated with the input position. + bool IsEmpty(size_t inputPositionIndex) const; + + //! Return the total number of output points generated. + //! @return The total number of output points generated. + size_t GetSize() const; + + //! Return the total number of output points generated from a specific input position index. + //! @param inputPositionIndex - The input position to look for output points for. + //! @return The total number of output points generated from a specific input position index. + size_t GetSize(size_t inputPositionIndex) const; + + //! Return the total number of input positions. + //! Normally the caller would already be expected to know this, but in the case of using region-based queries, the number of + //! input positions might not be entirely obvious. + //! @return The total number of input positions used to generate the outputs. + size_t GetInputPositionSize() const + { + return m_inputPositionSize; + } + + //! Enumerate every surface point and call a callback for each point found. + //! Note: There is no guaranteed order to which the points will be enumerated. + //! @pointCallback - The method to call with each surface point. + void EnumeratePoints( + AZStd::function + pointCallback) const; + + //! Enumerate every surface point for a given input position and call a callback for each point found. + //! Note: There is no guaranteed order to which the points will be enumerated. + //! @inputPositionIndex - The input position to get the outputs for. + //! @pointCallback - The method to call with each surface point. + void EnumeratePoints( + size_t inputPositionIndex, + AZStd::function pointCallback) const; + + //! Get the surface point with the highest Z value for a given input position. + //! @inputPositionIndex - The input position to get the highest surface point for. + //! @return The surface point with the highest Z value for the given input position. + AzFramework::SurfaceData::SurfacePoint GetHighestSurfacePoint(size_t inputPositionIndex) const; + + //! Get the AABB that encapsulates all of the generated output surface points. + //! @return The AABB surrounding all the output surface points. + AZ::Aabb GetSurfacePointAabb() const + { + return m_surfacePointBounds; + } + + protected: + // Remove any output surface points that don't contain any of the provided surface tags. + void FilterPoints(AZStd::span desiredTags); + + // Get the input position index associated with a specific input position. + size_t GetInPositionIndexFromPosition(const AZ::Vector3& inPosition) const; + + // Get the first entry in the sortedSurfacePointIndices list for the given input position index. + size_t GetSurfacePointStartIndexFromInPositionIndex(size_t inPositionIndex) const; + + // During list construction, keep track of the tags to filter the output points to. + // These will be used at the end of list construction to remove any output points that don't contain any of these tags. + // (If the list is empty, all output points will be retained) + AZStd::span m_filterTags; + + // During list construction, keep track of all the input positions that we'll generate outputs for. + // Note that after construction is complete, we'll only know how *many* input positions, but not their values. + // This keeps us from copying data that the caller should already have. We can't assume the lifetime of that data though, + // so we won't hold on to the span<> after construction. + AZStd::span m_inputPositions; + + // The total number of input positions that we have. We keep this value separately so that we can still know the quantity + // after list construction when our m_inputPositions span<> has become invalid. + size_t m_inputPositionSize = 0; + + // The last input position index that we used when adding points. + // This is used by GetInPositionIndexFromPosition() as an optimization to reduce search times for converting input positions + // to indices without needing to construct a separate search structure. + // Because we know surface points will get added in input position order, we'll always start looking for our next input position + // with the last one we used. + mutable size_t m_lastInputPositionIndex = 0; + + // This list is the size of m_inputPositions.size() and contains the number of output surface points that we've generated for + // each input point. + AZStd::vector m_numSurfacePointsPerInput; + + // The AABB surrounding all the surface points. We build this up incrementally as we add each surface point into the list. + AZ::Aabb m_surfacePointBounds = AZ::Aabb::CreateNull(); + + // The maximum number of output points that can be generated for each input. + size_t m_maxSurfacePointsPerInput = 0; + + // State tracker to determine whether or not the list is currently under construction. + // This is used to verify that the construction APIs are only used during construction, and the query APIs are only used + // after construction is complete. + bool m_listIsBeingConstructed = false; + + // List of lists that's used to index into our storage vectors for all the surface point data. + // The surface points are stored sequentially in creation order in the storage vectors. + // This should be thought of as a nested array - m_sortedSurfacePointIndices[m_inputPositionSize][m_maxSurfacePointsPerInput]. + // For each input position, the list of indices are kept sorted in decreasing Z order. + AZStd::vector m_sortedSurfacePointIndices; + + // Storage vectors for keeping track of all the created surface point data. + // These are kept in separate parallel vectors instead of a single struct so that it's possible to pass just specific data + // "channels" into other methods as span<> without having to pass the full struct into the span<>. Specifically, we want to be + // able to pass spans of the positions down through nesting gradient/surface calls. + AZStd::vector m_surfacePositionList; + AZStd::vector m_surfaceNormalList; + AZStd::vector m_surfaceWeightsList; + AZStd::vector m_surfaceCreatorIdList; + }; +} diff --git a/Gems/SurfaceData/Code/Include/SurfaceData/Tests/SurfaceDataTestMocks.h b/Gems/SurfaceData/Code/Include/SurfaceData/Tests/SurfaceDataTestMocks.h index bafe215402..e07fad98e8 100644 --- a/Gems/SurfaceData/Code/Include/SurfaceData/Tests/SurfaceDataTestMocks.h +++ b/Gems/SurfaceData/Code/Include/SurfaceData/Tests/SurfaceDataTestMocks.h @@ -205,14 +205,14 @@ namespace UnitTest } void GetSurfacePointsFromRegion([[maybe_unused]] const AZ::Aabb& inRegion, [[maybe_unused]] const AZ::Vector2 stepSize, [[maybe_unused]] const SurfaceData::SurfaceTagVector& desiredTags, - [[maybe_unused]] SurfaceData::SurfacePointLists& surfacePointListPerPosition) const override + [[maybe_unused]] SurfaceData::SurfacePointList& surfacePointListPerPosition) const override { } void GetSurfacePointsFromList( [[maybe_unused]] AZStd::span inPositions, [[maybe_unused]] const SurfaceData::SurfaceTagVector& desiredTags, - [[maybe_unused]] SurfaceData::SurfacePointLists& surfacePointLists) const override + [[maybe_unused]] SurfaceData::SurfacePointList& surfacePointLists) const override { } diff --git a/Gems/SurfaceData/Code/Source/Components/SurfaceDataColliderComponent.cpp b/Gems/SurfaceData/Code/Source/Components/SurfaceDataColliderComponent.cpp index 35f377bfe0..b145ef3181 100644 --- a/Gems/SurfaceData/Code/Source/Components/SurfaceDataColliderComponent.cpp +++ b/Gems/SurfaceData/Code/Source/Components/SurfaceDataColliderComponent.cpp @@ -185,8 +185,6 @@ namespace SurfaceData bool SurfaceDataColliderComponent::DoRayTrace(const AZ::Vector3& inPosition, bool queryPointOnly, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const { - AZStd::shared_lock lock(m_cacheMutex); - // test AABB as first pass to claim the point const AZ::Vector3 testPosition = AZ::Vector3( inPosition.GetX(), @@ -229,18 +227,45 @@ namespace SurfaceData void SurfaceDataColliderComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const { - AZ::Vector3 hitPosition; - AZ::Vector3 hitNormal; + GetSurfacePointsFromList(AZStd::span(&inPosition, 1), surfacePointList); + } - // We want a full raycast, so don't just query the start point. - constexpr bool queryPointOnly = false; + void SurfaceDataColliderComponent::GetSurfacePointsFromList( + AZStd::span inPositions, SurfacePointList& surfacePointList) const + { + AzPhysics::SimulatedBodyComponentRequestsBus::Event( + GetEntityId(), + [this, inPositions, &surfacePointList](AzPhysics::SimulatedBodyComponentRequestsBus::Events* simBody) + { + AZStd::shared_lock lock(m_cacheMutex); - if (DoRayTrace(inPosition, queryPointOnly, hitPosition, hitNormal)) - { - surfacePointList.AddSurfacePoint(GetEntityId(), hitPosition, hitNormal, m_newPointWeights); - } + AzPhysics::RayCastRequest request; + request.m_direction = -AZ::Vector3::CreateAxisZ(); + + for (auto& inPosition : inPositions) + { + // test AABB as first pass to claim the point + if (SurfaceData::AabbContains2D(m_colliderBounds, inPosition)) + { + // We're casting the ray to look for a collision, so start at the top of the collider and cast downwards + // the full height of the collider. + request.m_start = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_colliderBounds.GetMax().GetZ()); + request.m_distance = m_colliderBounds.GetExtents().GetZ(); + + AzPhysics::SceneQueryHit result = simBody->RayCast(request); + + if (result) + { + surfacePointList.AddSurfacePoint( + GetEntityId(), inPosition, result.m_position, result.m_normal, m_newPointWeights); + } + } + } + + }); } + void SurfaceDataColliderComponent::ModifySurfacePoints(SurfacePointList& surfacePointList) const { AZStd::shared_lock lock(m_cacheMutex); @@ -317,10 +342,11 @@ namespace SurfaceData providerRegistryEntry.m_entityId = GetEntityId(); providerRegistryEntry.m_bounds = m_colliderBounds; providerRegistryEntry.m_tags = m_configuration.m_providerTags; + providerRegistryEntry.m_maxPointsCreatedPerInput = 1; SurfaceDataRegistryEntry modifierRegistryEntry(providerRegistryEntry); modifierRegistryEntry.m_tags = m_configuration.m_modifierTags; - + modifierRegistryEntry.m_maxPointsCreatedPerInput = 0; if (!colliderValidBeforeUpdate && !colliderValidAfterUpdate) { diff --git a/Gems/SurfaceData/Code/Source/Components/SurfaceDataShapeComponent.cpp b/Gems/SurfaceData/Code/Source/Components/SurfaceDataShapeComponent.cpp index 973fca232a..251fece203 100644 --- a/Gems/SurfaceData/Code/Source/Components/SurfaceDataShapeComponent.cpp +++ b/Gems/SurfaceData/Code/Source/Components/SurfaceDataShapeComponent.cpp @@ -143,24 +143,48 @@ namespace SurfaceData } void SurfaceDataShapeComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const + { + GetSurfacePointsFromList(AZStd::span(&inPosition, 1), surfacePointList); + } + + void SurfaceDataShapeComponent::GetSurfacePointsFromList( + AZStd::span inPositions, SurfacePointList& surfacePointList) const { AZStd::shared_lock lock(m_cacheMutex); - if (m_shapeBoundsIsValid) + if (!m_shapeBoundsIsValid) { - const AZ::Vector3 rayOrigin = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_shapeBounds.GetMax().GetZ()); - const AZ::Vector3 rayDirection = -AZ::Vector3::CreateAxisZ(); - float intersectionDistance = 0.0f; - bool hitShape = false; - LmbrCentral::ShapeComponentRequestsBus::EventResult(hitShape, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::IntersectRay, rayOrigin, rayDirection, intersectionDistance); - if (hitShape) - { - AZ::Vector3 position = rayOrigin + intersectionDistance * rayDirection; - surfacePointList.AddSurfacePoint(GetEntityId(), position, AZ::Vector3::CreateAxisZ(), m_newPointWeights); - } + return; } + + LmbrCentral::ShapeComponentRequestsBus::Event( + GetEntityId(), + [this, inPositions, &surfacePointList](LmbrCentral::ShapeComponentRequestsBus::Events* shape) + { + const AZ::Vector3 rayDirection = -AZ::Vector3::CreateAxisZ(); + + // Shapes don't currently have a way to query normals at a point intersection, so we'll just return a Z-up normal + // until they get support for it. + const AZ::Vector3 surfacePointNormal = AZ::Vector3::CreateAxisZ(); + + for (auto& inPosition : inPositions) + { + if (SurfaceData::AabbContains2D(m_shapeBounds, inPosition)) + { + const AZ::Vector3 rayOrigin = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_shapeBounds.GetMax().GetZ()); + float intersectionDistance = 0.0f; + bool hitShape = shape->IntersectRay(rayOrigin, rayDirection, intersectionDistance); + if (hitShape) + { + AZ::Vector3 position = rayOrigin + intersectionDistance * rayDirection; + surfacePointList.AddSurfacePoint(GetEntityId(), inPosition, position, surfacePointNormal, m_newPointWeights); + } + } + } + }); } + void SurfaceDataShapeComponent::ModifySurfacePoints(SurfacePointList& surfacePointList) const { AZStd::shared_lock lock(m_cacheMutex); @@ -238,9 +262,11 @@ namespace SurfaceData providerRegistryEntry.m_entityId = GetEntityId(); providerRegistryEntry.m_bounds = m_shapeBounds; providerRegistryEntry.m_tags = m_configuration.m_providerTags; + providerRegistryEntry.m_maxPointsCreatedPerInput = 1; SurfaceDataRegistryEntry modifierRegistryEntry(providerRegistryEntry); modifierRegistryEntry.m_tags = m_configuration.m_modifierTags; + modifierRegistryEntry.m_maxPointsCreatedPerInput = 0; if (shapeValidBeforeUpdate && shapeValidAfterUpdate) { diff --git a/Gems/SurfaceData/Code/Source/SurfaceDataSystemComponent.cpp b/Gems/SurfaceData/Code/Source/SurfaceDataSystemComponent.cpp index 7dc6711b72..572b330579 100644 --- a/Gems/SurfaceData/Code/Source/SurfaceDataSystemComponent.cpp +++ b/Gems/SurfaceData/Code/Source/SurfaceDataSystemComponent.cpp @@ -205,54 +205,12 @@ namespace SurfaceData void SurfaceDataSystemComponent::GetSurfacePoints(const AZ::Vector3& inPosition, const SurfaceTagVector& desiredTags, SurfacePointList& surfacePointList) const { - const bool useTagFilters = HasValidTags(desiredTags); - const bool hasModifierTags = useTagFilters && HasAnyMatchingTags(desiredTags, m_registeredModifierTags); - - AZStd::shared_lock registrationLock(m_registrationMutex); - - surfacePointList.Clear(); - surfacePointList.ReserveSpace(m_registeredSurfaceDataProviders.size()); - - //gather all intersecting points - for (const auto& entryPair : m_registeredSurfaceDataProviders) - { - const AZ::u32 entryAddress = entryPair.first; - const SurfaceDataRegistryEntry& entry = entryPair.second; - if (!entry.m_bounds.IsValid() || AabbContains2D(entry.m_bounds, inPosition)) - { - if (!useTagFilters || hasModifierTags || HasAnyMatchingTags(desiredTags, entry.m_tags)) - { - SurfaceDataProviderRequestBus::Event(entryAddress, &SurfaceDataProviderRequestBus::Events::GetSurfacePoints, inPosition, surfacePointList); - } - } - } - - if (!surfacePointList.IsEmpty()) - { - //modify or annotate reported points - for (const auto& entryPair : m_registeredSurfaceDataModifiers) - { - const AZ::u32 entryAddress = entryPair.first; - const SurfaceDataRegistryEntry& entry = entryPair.second; - if (!entry.m_bounds.IsValid() || AabbContains2D(entry.m_bounds, inPosition)) - { - SurfaceDataModifierRequestBus::Event(entryAddress, &SurfaceDataModifierRequestBus::Events::ModifySurfacePoints, surfacePointList); - } - } - - // After we've finished creating and annotating all the surface points, combine any points together that have effectively the - // same XY coordinates and extremely similar Z values. This produces results that are sorted in decreasing Z order. - // Also, this filters out any remaining points that don't match the desired tag list. This can happen when a surface provider - // doesn't add a desired tag, and a surface modifier has the *potential* to add it, but then doesn't. - if (useTagFilters) - { - surfacePointList.FilterPoints(desiredTags); - } - } + GetSurfacePointsFromListInternal( + AZStd::span(&inPosition, 1), AZ::Aabb::CreateFromPoint(inPosition), desiredTags, surfacePointList); } void SurfaceDataSystemComponent::GetSurfacePointsFromRegion(const AZ::Aabb& inRegion, const AZ::Vector2 stepSize, - const SurfaceTagVector& desiredTags, SurfacePointLists& surfacePointLists) const + const SurfaceTagVector& desiredTags, SurfacePointList& surfacePointLists) const { const size_t totalQueryPositions = aznumeric_cast(ceil(inRegion.GetXExtent() / stepSize.GetX())) * aznumeric_cast(ceil(inRegion.GetYExtent() / stepSize.GetY())); @@ -270,47 +228,89 @@ namespace SurfaceData } } - GetSurfacePointsFromList(inPositions, desiredTags, surfacePointLists); + GetSurfacePointsFromListInternal(inPositions, inRegion, desiredTags, surfacePointLists); } void SurfaceDataSystemComponent::GetSurfacePointsFromList( - AZStd::span inPositions, const SurfaceTagVector& desiredTags, SurfacePointLists& surfacePointLists) const + AZStd::span inPositions, + const SurfaceTagVector& desiredTags, + SurfacePointList& surfacePointLists) const { - AZStd::shared_lock registrationLock(m_registrationMutex); - - const size_t totalQueryPositions = inPositions.size(); - - surfacePointLists.clear(); - surfacePointLists.resize(totalQueryPositions); - - for (auto& surfacePointList : surfacePointLists) + AZ::Aabb inBounds = AZ::Aabb::CreateNull(); + for (auto& position : inPositions) { - surfacePointList.ReserveSpace(m_registeredSurfaceDataProviders.size()); + inBounds.AddPoint(position); } + GetSurfacePointsFromListInternal(inPositions, inBounds, desiredTags, surfacePointLists); + } + + void SurfaceDataSystemComponent::GetSurfacePointsFromListInternal( + AZStd::span inPositions, const AZ::Aabb& inPositionBounds, + const SurfaceTagVector& desiredTags, SurfacePointList& surfacePointLists) const + { + AZStd::shared_lock registrationLock(m_registrationMutex); + const bool useTagFilters = HasValidTags(desiredTags); const bool hasModifierTags = useTagFilters && HasAnyMatchingTags(desiredTags, m_registeredModifierTags); - // Loop through each data provider, and query all the points for each one. This allows us to check the tags and the overall - // AABB bounds just once per provider, instead of once per point. It also allows for an eventual optimization in which we could - // send the list of points directly into each SurfaceDataProvider. - for (const auto& [providerHandle, provider] : m_registeredSurfaceDataProviders) + // Clear our output structure. + surfacePointLists.Clear(); + + auto ProviderIsApplicable = [useTagFilters, hasModifierTags, &desiredTags, &inPositionBounds] + (const SurfaceDataRegistryEntry& provider) -> bool { bool hasInfiniteBounds = !provider.m_bounds.IsValid(); + // Only allow surface providers that match our tag filters. However, if we aren't using tag filters, + // or if there's at least one surface modifier that can *add* a filtered tag to a created point, then + // allow all the surface providers. if (!useTagFilters || hasModifierTags || HasAnyMatchingTags(desiredTags, provider.m_tags)) { - for (size_t index = 0; index < totalQueryPositions; index++) + // Only allow surface providers that overlap the input position area. + if (hasInfiniteBounds || AabbOverlaps2D(provider.m_bounds, inPositionBounds)) { - bool inBounds = hasInfiniteBounds || AabbContains2D(provider.m_bounds, inPositions[index]); - if (inBounds) - { - SurfaceDataProviderRequestBus::Event( - providerHandle, &SurfaceDataProviderRequestBus::Events::GetSurfacePoints, - inPositions[index], surfacePointLists[index]); - } + return true; } } + + return false; + }; + + // Gather up the subset of surface providers that overlap the input positions. + size_t maxPointsCreatedPerInput = 0; + for (const auto& [providerHandle, provider] : m_registeredSurfaceDataProviders) + { + if (ProviderIsApplicable(provider)) + { + maxPointsCreatedPerInput += provider.m_maxPointsCreatedPerInput; + } + } + + // If we don't have any surface providers that will create any new surface points, then there's nothing more to do. + if (maxPointsCreatedPerInput == 0) + { + return; + } + + // Notify our output structure that we're starting to build up the list of output points. + // This will reserve memory and allocate temporary structures to help build up the list efficiently. + AZStd::span tagFilters; + if (useTagFilters) + { + tagFilters = desiredTags; + } + surfacePointLists.StartListConstruction(inPositions, maxPointsCreatedPerInput, tagFilters); + + // Loop through each data provider and generate surface points from the set of input positions. + // Any generated points that have the same XY coordinates and extremely similar Z values will get combined together. + for (const auto& [providerHandle, provider] : m_registeredSurfaceDataProviders) + { + if (ProviderIsApplicable(provider)) + { + SurfaceDataProviderRequestBus::Event( + providerHandle, &SurfaceDataProviderRequestBus::Events::GetSurfacePointsFromList, inPositions, surfacePointLists); + } } // Once we have our list of surface points created, run through the list of surface data modifiers to potentially add @@ -318,43 +318,28 @@ namespace SurfaceData // create new surface points, but surface data *modifiers* simply annotate points that have already been created. The modifiers // are used to annotate points that occur within a volume. A common example is marking points as "underwater" for points that occur // within a water volume. - for (const auto& entryPair : m_registeredSurfaceDataModifiers) + for (const auto& [modifierHandle, modifier] : m_registeredSurfaceDataModifiers) { - const SurfaceDataRegistryEntry& entry = entryPair.second; - bool hasInfiniteBounds = !entry.m_bounds.IsValid(); + bool hasInfiniteBounds = !modifier.m_bounds.IsValid(); - for (size_t index = 0; index < totalQueryPositions; index++) + if (hasInfiniteBounds || AabbOverlaps2D(modifier.m_bounds, surfacePointLists.GetSurfacePointAabb())) { - const auto& inPosition = inPositions[index]; - SurfacePointList& surfacePointList = surfacePointLists[index]; - if (!surfacePointList.IsEmpty()) - { - if (hasInfiniteBounds || AabbContains2D(entry.m_bounds, inPosition)) - { - SurfaceDataModifierRequestBus::Event( - entryPair.first, &SurfaceDataModifierRequestBus::Events::ModifySurfacePoints, - surfacePointList); - } - } + SurfaceDataModifierRequestBus::Event( + modifierHandle, &SurfaceDataModifierRequestBus::Events::ModifySurfacePoints, + surfacePointLists); } } - // After we've finished creating and annotating all the surface points, combine any points together that have effectively the - // same XY coordinates and extremely similar Z values. This produces results that are sorted in decreasing Z order. - // Also, this filters out any remaining points that don't match the desired tag list. This can happen when a surface provider + // Notify the output structure that we're done building up the list. + // This will filter out any remaining points that don't match the desired tag list. This can happen when a surface provider // doesn't add a desired tag, and a surface modifier has the *potential* to add it, but then doesn't. - if (useTagFilters) - { - for (auto& surfacePointList : surfacePointLists) - { - surfacePointList.FilterPoints(desiredTags); - } - } - + // It may also compact the memory and free any temporary structures. + surfacePointLists.EndListConstruction(); } SurfaceDataRegistryHandle SurfaceDataSystemComponent::RegisterSurfaceDataProviderInternal(const SurfaceDataRegistryEntry& entry) { + AZ_Assert(entry.m_maxPointsCreatedPerInput > 0, "Surface data providers should always create at least 1 point."); AZStd::unique_lock registrationLock(m_registrationMutex); SurfaceDataRegistryHandle handle = ++m_registeredSurfaceDataProviderHandleCounter; m_registeredSurfaceDataProviders[handle] = entry; @@ -376,6 +361,7 @@ namespace SurfaceData bool SurfaceDataSystemComponent::UpdateSurfaceDataProviderInternal(const SurfaceDataRegistryHandle& handle, const SurfaceDataRegistryEntry& entry, AZ::Aabb& oldBounds) { + AZ_Assert(entry.m_maxPointsCreatedPerInput > 0, "Surface data providers should always create at least 1 point."); AZStd::unique_lock registrationLock(m_registrationMutex); auto entryItr = m_registeredSurfaceDataProviders.find(handle); if (entryItr != m_registeredSurfaceDataProviders.end()) @@ -389,6 +375,7 @@ namespace SurfaceData SurfaceDataRegistryHandle SurfaceDataSystemComponent::RegisterSurfaceDataModifierInternal(const SurfaceDataRegistryEntry& entry) { + AZ_Assert(entry.m_maxPointsCreatedPerInput == 0, "Surface data modifiers cannot create any points."); AZStd::unique_lock registrationLock(m_registrationMutex); SurfaceDataRegistryHandle handle = ++m_registeredSurfaceDataModifierHandleCounter; m_registeredSurfaceDataModifiers[handle] = entry; @@ -411,6 +398,7 @@ namespace SurfaceData bool SurfaceDataSystemComponent::UpdateSurfaceDataModifierInternal(const SurfaceDataRegistryHandle& handle, const SurfaceDataRegistryEntry& entry, AZ::Aabb& oldBounds) { + AZ_Assert(entry.m_maxPointsCreatedPerInput == 0, "Surface data modifiers cannot create any points."); AZStd::unique_lock registrationLock(m_registrationMutex); auto entryItr = m_registeredSurfaceDataModifiers.find(handle); if (entryItr != m_registeredSurfaceDataModifiers.end()) diff --git a/Gems/SurfaceData/Code/Source/SurfaceDataTypes.cpp b/Gems/SurfaceData/Code/Source/SurfaceDataTypes.cpp index 3d6378aa7c..06dda90992 100644 --- a/Gems/SurfaceData/Code/Source/SurfaceDataTypes.cpp +++ b/Gems/SurfaceData/Code/Source/SurfaceDataTypes.cpp @@ -7,7 +7,6 @@ */ #include -#include namespace SurfaceData { @@ -118,7 +117,7 @@ namespace SurfaceData return FindTag(sampleTag) != m_weights.end(); } - bool SurfaceTagWeights::HasAnyMatchingTags(const SurfaceTagVector& sampleTags) const + bool SurfaceTagWeights::HasAnyMatchingTags(AZStd::span sampleTags) const { for (const auto& sampleTag : sampleTags) { @@ -137,7 +136,7 @@ namespace SurfaceData return weightEntry != m_weights.end() && weightMin <= weightEntry->m_weight && weightMax >= weightEntry->m_weight; } - bool SurfaceTagWeights::HasAnyMatchingTags(const SurfaceTagVector& sampleTags, float weightMin, float weightMax) const + bool SurfaceTagWeights::HasAnyMatchingTags(AZStd::span sampleTags, float weightMin, float weightMax) const { for (const auto& sampleTag : sampleTags) { @@ -169,151 +168,4 @@ namespace SurfaceData // The tag wasn't found, so return end(). return m_weights.end(); } - - - SurfacePointList::SurfacePointList(AZStd::initializer_list surfacePoints) - { - ReserveSpace(surfacePoints.size()); - - for (auto& point : surfacePoints) - { - SurfaceTagWeights weights(point.m_surfaceTags); - AddSurfacePoint(AZ::EntityId(), point.m_position, point.m_normal, weights); - } - } - - void SurfacePointList::AddSurfacePoint(const AZ::EntityId& entityId, - const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceTagWeights& masks) - { - // When adding a surface point, we'll either merge it with a similar existing point, or else add it in order of - // decreasing Z, so that our final results are sorted. - - for (size_t index = 0; index < m_surfacePositionList.size(); ++index) - { - // (Someday we should add a configurable tolerance for comparison) - if (m_surfacePositionList[index].IsClose(position) && m_surfaceNormalList[index].IsClose(normal)) - { - // consolidate points with similar attributes by adding masks/weights to the similar point instead of adding a new one. - m_surfaceWeightsList[index].AddSurfaceTagWeights(masks); - return; - } - else if (m_surfacePositionList[index].GetZ() < position.GetZ()) - { - m_pointBounds.AddPoint(position); - m_surfacePositionList.insert(m_surfacePositionList.begin() + index, position); - m_surfaceNormalList.insert(m_surfaceNormalList.begin() + index, normal); - m_surfaceWeightsList.insert(m_surfaceWeightsList.begin() + index, masks); - m_surfaceCreatorIdList.insert(m_surfaceCreatorIdList.begin() + index, entityId); - return; - } - } - - // The point wasn't merged and the sort puts it at the end, so just add the point to the end of the list. - m_pointBounds.AddPoint(position); - m_surfacePositionList.emplace_back(position); - m_surfaceNormalList.emplace_back(normal); - m_surfaceWeightsList.emplace_back(masks); - m_surfaceCreatorIdList.emplace_back(entityId); - } - - void SurfacePointList::Clear() - { - m_surfacePositionList.clear(); - m_surfaceNormalList.clear(); - m_surfaceWeightsList.clear(); - m_surfaceCreatorIdList.clear(); - } - - void SurfacePointList::ReserveSpace(size_t maxPointsPerInput) - { - AZ_Assert(m_surfacePositionList.empty(), "Trying to reserve space on a list that is already being used."); - - m_surfaceCreatorIdList.reserve(maxPointsPerInput); - m_surfacePositionList.reserve(maxPointsPerInput); - m_surfaceNormalList.reserve(maxPointsPerInput); - m_surfaceWeightsList.reserve(maxPointsPerInput); - } - - bool SurfacePointList::IsEmpty() const - { - return m_surfacePositionList.empty(); - } - - size_t SurfacePointList::GetSize() const - { - return m_surfacePositionList.size(); - } - - void SurfacePointList::EnumeratePoints( - AZStd::function - pointCallback) const - { - for (size_t index = 0; index < m_surfacePositionList.size(); index++) - { - if (!pointCallback(m_surfacePositionList[index], m_surfaceNormalList[index], m_surfaceWeightsList[index])) - { - break; - } - } - } - - void SurfacePointList::ModifySurfaceWeights( - const AZ::EntityId& currentEntityId, - AZStd::function modificationWeightCallback) - { - for (size_t index = 0; index < m_surfacePositionList.size(); index++) - { - if (m_surfaceCreatorIdList[index] != currentEntityId) - { - modificationWeightCallback(m_surfacePositionList[index], m_surfaceWeightsList[index]); - } - } - } - - AzFramework::SurfaceData::SurfacePoint SurfacePointList::GetHighestSurfacePoint() const - { - AzFramework::SurfaceData::SurfacePoint point; - point.m_position = m_surfacePositionList.front(); - point.m_normal = m_surfaceNormalList.front(); - point.m_surfaceTags = m_surfaceWeightsList.front().GetSurfaceTagWeightList(); - - return point; - } - - void SurfacePointList::FilterPoints(const SurfaceTagVector& desiredTags) - { - // Filter out any points that don't match our search tags. - // This has to be done after the Surface Modifiers have processed the points, not at point insertion time, because - // Surface Modifiers add tags to existing points. - size_t listSize = m_surfacePositionList.size(); - size_t index = 0; - for (; index < listSize; index++) - { - if (!m_surfaceWeightsList[index].HasAnyMatchingTags(desiredTags)) - { - break; - } - } - - if (index != listSize) - { - size_t next = index + 1; - for (; next < listSize; ++next) - { - if (m_surfaceWeightsList[index].HasAnyMatchingTags(desiredTags)) - { - m_surfaceCreatorIdList[index] = m_surfaceCreatorIdList[next]; - m_surfacePositionList[index] = m_surfacePositionList[next]; - m_surfaceNormalList[index] = m_surfaceNormalList[next]; - m_surfaceWeightsList[index] = m_surfaceWeightsList[next]; - ++index; - } - } - - m_surfaceCreatorIdList.resize(index); - m_surfacePositionList.resize(index); - m_surfaceNormalList.resize(index); - m_surfaceWeightsList.resize(index); - } - } } diff --git a/Gems/SurfaceData/Code/Source/SurfacePointList.cpp b/Gems/SurfaceData/Code/Source/SurfacePointList.cpp new file mode 100644 index 0000000000..fea06e3ee5 --- /dev/null +++ b/Gems/SurfaceData/Code/Source/SurfacePointList.cpp @@ -0,0 +1,365 @@ +/* + * 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 SurfaceData +{ + size_t SurfacePointList::GetInPositionIndexFromPosition(const AZ::Vector3& inPosition) const + { + // Given an input position, find the input position index that's associated with it. + // We'll bias towards always having a position that's the same or further in our input list than before, + // so we'll do a linear search that starts with the last input position we used, and goes forward (and wraps around) + // until we've searched them all. + // Our expectation is that most of the time, we'll only have to compare 0-1 input positions. + + size_t inPositionIndex = m_lastInputPositionIndex; + bool foundMatch = false; + for (size_t indexCounter = 0; indexCounter < m_inputPositions.size(); indexCounter++) + { + if (m_inputPositions[inPositionIndex] == inPosition) + { + foundMatch = true; + break; + } + + inPositionIndex = (inPositionIndex + 1) % m_inputPositions.size(); + } + + AZ_Assert( + foundMatch, + "Couldn't find input position: (%0.7f, %0.7f, %0.7f), m_lastInputPositionIndex = %zu, m_inputPositions.size() = %zu", + inPosition.GetX(), inPosition.GetY(), inPosition.GetZ(), m_lastInputPositionIndex, m_inputPositions.size()); + + m_lastInputPositionIndex = inPositionIndex; + return inPositionIndex; + } + + size_t SurfacePointList::GetSurfacePointStartIndexFromInPositionIndex(size_t inPositionIndex) const + { + // Index to the first output surface point for this input position. + return inPositionIndex * m_maxSurfacePointsPerInput; + } + + SurfacePointList::SurfacePointList(AZStd::span surfacePoints) + { + // Construct and finalize the list with the set of passed-in surface points. + // This is primarily a convenience for unit tests. + StartListConstruction(surfacePoints); + EndListConstruction(); + } + + void SurfacePointList::StartListConstruction(AZStd::span surfacePoints) + { + // Construct the list with the set of passed-in surface points but don't finalize it. + // This is primarily a convenience for unit tests that want to test surface modifiers with specific inputs. + + surfacePoints.begin(); + StartListConstruction(AZStd::span(&(surfacePoints.begin()->m_position), 1), surfacePoints.size(), {}); + + for (auto& point : surfacePoints) + { + SurfaceTagWeights weights(point.m_surfaceTags); + AddSurfacePoint(AZ::EntityId(), point.m_position, point.m_position, point.m_normal, weights); + } + } + + void SurfacePointList::StartListConstruction( + AZStd::span inPositions, size_t maxPointsPerInput, AZStd::span filterTags) + { + AZ_Assert(!m_listIsBeingConstructed, "Trying to start list construction on a list currently under construction."); + AZ_Assert(m_surfacePositionList.empty(), "Trying to reserve space on a list that is already being used."); + + Clear(); + + m_listIsBeingConstructed = true; + + // Save off working references to the data we'll need during list construction. + // These references need to remain valid during construction, but not afterwards. + m_filterTags = filterTags; + m_inputPositions = inPositions; + m_inputPositionSize = inPositions.size(); + m_maxSurfacePointsPerInput = maxPointsPerInput; + + size_t outputReserveSize = inPositions.size() * m_maxSurfacePointsPerInput; + + // Reserve enough space to have one value per input position, and initialize it to 0. + m_numSurfacePointsPerInput.resize(m_inputPositionSize); + + // Reserve enough space to have maxSurfacePointsPerInput entries per input position, and initialize them all to 0. + m_sortedSurfacePointIndices.resize(outputReserveSize); + + // Reserve enough space for all our possible output surface points, but don't initialize them. + m_surfaceCreatorIdList.reserve(outputReserveSize); + m_surfacePositionList.reserve(outputReserveSize); + m_surfaceNormalList.reserve(outputReserveSize); + m_surfaceWeightsList.reserve(outputReserveSize); + } + + void SurfacePointList::Clear() + { + m_listIsBeingConstructed = false; + + m_lastInputPositionIndex = 0; + m_inputPositionSize = 0; + m_maxSurfacePointsPerInput = 0; + + m_filterTags = {}; + m_inputPositions = {}; + + m_sortedSurfacePointIndices.clear(); + m_numSurfacePointsPerInput.clear(); + m_surfacePositionList.clear(); + m_surfaceNormalList.clear(); + m_surfaceWeightsList.clear(); + m_surfaceCreatorIdList.clear(); + + m_surfacePointBounds = AZ::Aabb::CreateNull(); + } + + void SurfacePointList::AddSurfacePoint( + const AZ::EntityId& entityId, const AZ::Vector3& inPosition, + const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceTagWeights& masks) + { + AZ_Assert(m_listIsBeingConstructed, "Trying to add surface points to a SurfacePointList that isn't under construction."); + + // Find the inPositionIndex that matches the inPosition. + size_t inPositionIndex = GetInPositionIndexFromPosition(inPosition); + + // Find the first SurfacePoint that either matches the inPosition, or that starts the range for the next inPosition after this one. + size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inPositionIndex); + + // When adding a surface point, we'll either merge it with a similar existing point, or else add it in order of + // decreasing Z, so that our final results are sorted. + size_t surfacePointInsertIndex = surfacePointStartIndex; + + for (; surfacePointInsertIndex < (surfacePointStartIndex + m_numSurfacePointsPerInput[inPositionIndex]); ++surfacePointInsertIndex) + { + // (Someday we should add a configurable tolerance for comparison) + if (m_surfacePositionList[m_sortedSurfacePointIndices[surfacePointInsertIndex]].IsClose(position) && + m_surfaceNormalList[m_sortedSurfacePointIndices[surfacePointInsertIndex]].IsClose(normal)) + { + // consolidate points with similar attributes by adding masks/weights to the similar point instead of adding a new one. + m_surfaceWeightsList[m_sortedSurfacePointIndices[surfacePointInsertIndex]].AddSurfaceTagWeights(masks); + return; + } + else if (m_surfacePositionList[m_sortedSurfacePointIndices[surfacePointInsertIndex]].GetZ() < position.GetZ()) + { + break; + } + } + + // If we've made it here, we're adding the point, not merging it. + + // Verify we aren't adding more points than expected. + AZ_Assert(m_numSurfacePointsPerInput[inPositionIndex] < m_maxSurfacePointsPerInput, "Adding too many surface points."); + + // Expand our output AABB to include this point. + m_surfacePointBounds.AddPoint(position); + + // If this isn't the first output for this input position, shift our sorted indices for this input position to make room for + // the new entry. + if (m_numSurfacePointsPerInput[inPositionIndex] > 0) + { + size_t startIndex = surfacePointInsertIndex; + size_t endIndex = surfacePointStartIndex + m_numSurfacePointsPerInput[inPositionIndex]; + + AZStd::move_backward( + m_sortedSurfacePointIndices.begin() + startIndex, m_sortedSurfacePointIndices.begin() + endIndex, + m_sortedSurfacePointIndices.begin() + endIndex + 1); + } + + m_numSurfacePointsPerInput[inPositionIndex]++; + + // Insert the new sorted index that references into our storage vectors. + m_sortedSurfacePointIndices[surfacePointInsertIndex] = m_surfacePositionList.size(); + + // Add the new point to the back of our storage vectors. + m_surfacePositionList.emplace_back(position); + m_surfaceNormalList.emplace_back(normal); + m_surfaceWeightsList.emplace_back(masks); + m_surfaceCreatorIdList.emplace_back(entityId); + } + + void SurfacePointList::ModifySurfaceWeights( + const AZ::EntityId& currentEntityId, + AZStd::function modificationWeightCallback) + { + AZ_Assert(m_listIsBeingConstructed, "Trying to modify surface weights on a SurfacePointList that isn't under construction."); + + // For every valid output point, call the modification callback only if it doesn't match the entity that created the point. + for (size_t inputIndex = 0; (inputIndex < m_inputPositionSize); inputIndex++) + { + size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inputIndex); + for (size_t index = surfacePointStartIndex; (index < (surfacePointStartIndex + m_numSurfacePointsPerInput[inputIndex])); + index++) + { + if (m_surfaceCreatorIdList[m_sortedSurfacePointIndices[index]] != currentEntityId) + { + modificationWeightCallback( + m_surfacePositionList[m_sortedSurfacePointIndices[index]], + m_surfaceWeightsList[m_sortedSurfacePointIndices[index]]); + } + } + } + } + + void SurfacePointList::FilterPoints(AZStd::span desiredTags) + { + AZ_Assert(m_listIsBeingConstructed, "Trying to filter a SurfacePointList that isn't under construction."); + + // Filter out any points that don't match our search tags. + // This has to be done after the Surface Modifiers have processed the points, not at point insertion time, because + // Surface Modifiers add tags to existing points. + // The algorithm below is basically an "erase_if" that's operating across multiple storage vectors and using one level of + // indirection to keep our sorted indices valid. + // At some point we might want to consider modifying this to compact the final storage to the minimum needed. + for (size_t inputIndex = 0; (inputIndex < m_inputPositionSize); inputIndex++) + { + size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inputIndex); + size_t listSize = (surfacePointStartIndex + m_numSurfacePointsPerInput[inputIndex]); + size_t index = surfacePointStartIndex; + for (; index < listSize; index++) + { + if (!m_surfaceWeightsList[m_sortedSurfacePointIndices[index]].HasAnyMatchingTags(desiredTags)) + { + break; + } + } + + if (index != listSize) + { + size_t next = index + 1; + for (; next < listSize; ++next) + { + if (m_surfaceWeightsList[m_sortedSurfacePointIndices[index]].HasAnyMatchingTags(desiredTags)) + { + m_sortedSurfacePointIndices[index] = AZStd::move(m_sortedSurfacePointIndices[next]); + m_surfaceCreatorIdList[m_sortedSurfacePointIndices[index]] = + AZStd::move(m_surfaceCreatorIdList[m_sortedSurfacePointIndices[next]]); + m_surfacePositionList[m_sortedSurfacePointIndices[index]] = + AZStd::move(m_surfacePositionList[m_sortedSurfacePointIndices[next]]); + m_surfaceNormalList[m_sortedSurfacePointIndices[index]] = + AZStd::move(m_surfaceNormalList[m_sortedSurfacePointIndices[next]]); + m_surfaceWeightsList[m_sortedSurfacePointIndices[index]] = + AZStd::move(m_surfaceWeightsList[m_sortedSurfacePointIndices[next]]); + + m_numSurfacePointsPerInput[inputIndex]--; + + ++index; + } + } + } + } + } + + void SurfacePointList::EndListConstruction() + { + AZ_Assert(m_listIsBeingConstructed, "Trying to end list construction on a SurfacePointList that isn't under construction."); + + // Now that we've finished adding and modifying points, filter out any points that don't match the filterTags list, if we have one. + if (!m_filterTags.empty()) + { + FilterPoints(m_filterTags); + } + + m_listIsBeingConstructed = false; + m_inputPositions = {}; + m_filterTags = {}; + } + + bool SurfacePointList::IsEmpty() const + { + AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction."); + + return m_surfacePositionList.empty(); + } + + bool SurfacePointList::IsEmpty(size_t inputPositionIndex) const + { + AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction."); + + return (m_inputPositionSize == 0) || (m_numSurfacePointsPerInput[inputPositionIndex] == 0); + } + + size_t SurfacePointList::GetSize() const + { + AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction."); + + return m_surfacePositionList.size(); + } + + size_t SurfacePointList::GetSize(size_t inputPositionIndex) const + { + AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction."); + + return (m_inputPositionSize == 0) ? 0 : (m_numSurfacePointsPerInput[inputPositionIndex]); + } + + void SurfacePointList::EnumeratePoints( + size_t inputPositionIndex, + AZStd::function + pointCallback) const + { + AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction."); + + size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inputPositionIndex); + for (size_t index = surfacePointStartIndex; + (index < (surfacePointStartIndex + m_numSurfacePointsPerInput[inputPositionIndex])); index++) + { + if (!pointCallback( + m_surfacePositionList[m_sortedSurfacePointIndices[index]], m_surfaceNormalList[m_sortedSurfacePointIndices[index]], + m_surfaceWeightsList[m_sortedSurfacePointIndices[index]])) + { + break; + } + } + } + + void SurfacePointList::EnumeratePoints( + AZStd::function + pointCallback) const + { + AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction."); + + for (size_t inputIndex = 0; (inputIndex < m_inputPositionSize); inputIndex++) + { + size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inputIndex); + for (size_t index = surfacePointStartIndex; (index < (surfacePointStartIndex + m_numSurfacePointsPerInput[inputIndex])); + index++) + { + if (!pointCallback( + inputIndex, m_surfacePositionList[m_sortedSurfacePointIndices[index]], + m_surfaceNormalList[m_sortedSurfacePointIndices[index]], m_surfaceWeightsList[m_sortedSurfacePointIndices[index]])) + { + break; + } + } + } + } + + AzFramework::SurfaceData::SurfacePoint SurfacePointList::GetHighestSurfacePoint([[maybe_unused]] size_t inputPositionIndex) const + { + AZ_Assert(!m_listIsBeingConstructed, "Trying to query a SurfacePointList that's still under construction."); + + if (m_numSurfacePointsPerInput[inputPositionIndex] == 0) + { + return {}; + } + + size_t surfacePointStartIndex = GetSurfacePointStartIndexFromInPositionIndex(inputPositionIndex); + AzFramework::SurfaceData::SurfacePoint point; + point.m_position = m_surfacePositionList[m_sortedSurfacePointIndices[surfacePointStartIndex]]; + point.m_normal = m_surfaceNormalList[m_sortedSurfacePointIndices[surfacePointStartIndex]]; + point.m_surfaceTags = m_surfaceWeightsList[m_sortedSurfacePointIndices[surfacePointStartIndex]].GetSurfaceTagWeightList(); + + return point; + } + +} diff --git a/Gems/SurfaceData/Code/Tests/SurfaceDataBenchmarks.cpp b/Gems/SurfaceData/Code/Tests/SurfaceDataBenchmarks.cpp index becb91f0ce..549e8b8bea 100644 --- a/Gems/SurfaceData/Code/Tests/SurfaceDataBenchmarks.cpp +++ b/Gems/SurfaceData/Code/Tests/SurfaceDataBenchmarks.cpp @@ -181,7 +181,7 @@ namespace UnitTest SurfaceData::SurfaceTagVector filterTags = CreateBenchmarkTagFilterList(); // Query every point in our world at 1 meter intervals. - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { // This is declared outside the loop so that the list of points doesn't fully reallocate on every query. SurfaceData::SurfacePointList points; @@ -211,9 +211,9 @@ namespace UnitTest SurfaceData::SurfaceTagVector filterTags = CreateBenchmarkTagFilterList(); // Query every point in our world at 1 meter intervals. - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { - SurfaceData::SurfacePointLists points; + SurfaceData::SurfacePointList points; AZ::Aabb inRegion = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(worldSize)); AZ::Vector2 stepSize(1.0f); @@ -235,7 +235,7 @@ namespace UnitTest SurfaceData::SurfaceTagVector filterTags = CreateBenchmarkTagFilterList(); // Query every point in our world at 1 meter intervals. - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { AZStd::vector queryPositions; queryPositions.reserve(worldSizeInt * worldSizeInt); @@ -248,7 +248,7 @@ namespace UnitTest } } - SurfaceData::SurfacePointLists points; + SurfaceData::SurfacePointList points; SurfaceData::SurfaceDataSystemRequestBus::Broadcast( &SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromList, queryPositions, filterTags, points); @@ -289,7 +289,7 @@ namespace UnitTest tag = randomGenerator.GetRandom(); } - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { // We'll benchmark this two ways: // 1. We clear each time, which means each AddSurfaceWeightIfGreater call will search the whole list then add. @@ -340,7 +340,7 @@ namespace UnitTest comparisonTags.emplace_back(tag ^ 0x01); } - for (auto _ : state) + for ([[maybe_unused]] auto _ : state) { // Test to see if any of our tags match. // All of comparison tags should get compared against all of the added tags. diff --git a/Gems/SurfaceData/Code/Tests/SurfaceDataColliderComponentTest.cpp b/Gems/SurfaceData/Code/Tests/SurfaceDataColliderComponentTest.cpp index aca5a53f4d..8724227960 100644 --- a/Gems/SurfaceData/Code/Tests/SurfaceDataColliderComponentTest.cpp +++ b/Gems/SurfaceData/Code/Tests/SurfaceDataColliderComponentTest.cpp @@ -135,14 +135,17 @@ namespace UnitTest // Call GetSurfacePoints and verify the results SurfaceData::SurfacePointList pointList; + pointList.StartListConstruction(AZStd::span(&queryPoint, 1), 1, {}); SurfaceData::SurfaceDataProviderRequestBus::Event(providerHandle, &SurfaceData::SurfaceDataProviderRequestBus::Events::GetSurfacePoints, queryPoint, pointList); + pointList.EndListConstruction(); + if (pointOnProvider) { ASSERT_EQ(pointList.GetSize(), 1); - pointList.EnumeratePoints( - [this, expectedOutput]( - const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool + pointList.EnumeratePoints([this, expectedOutput]( + [[maybe_unused]] size_t inPositionIndex, const AZ::Vector3& position, + const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool { EXPECT_TRUE(SurfacePointsAreEqual(position, normal, masks, expectedOutput)); return true; @@ -185,12 +188,14 @@ namespace UnitTest // Call ModifySurfacePoints and verify the results // Add the surface point with a different entity ID than the entity doing the modification, so that the point doesn't get // filtered out. - SurfaceData::SurfacePointList pointList = { input }; + SurfaceData::SurfacePointList pointList; + pointList.StartListConstruction(AZStd::span(&input, 1)); SurfaceData::SurfaceDataModifierRequestBus::Event(modifierHandle, &SurfaceData::SurfaceDataModifierRequestBus::Events::ModifySurfacePoints, pointList); + pointList.EndListConstruction(); ASSERT_EQ(pointList.GetSize(), 1); - pointList.EnumeratePoints( - [this, expectedOutput]( - const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool + pointList.EnumeratePoints([this, expectedOutput]( + [[maybe_unused]] size_t inPositionIndex, const AZ::Vector3& position, + const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool { EXPECT_TRUE(SurfacePointsAreEqual(position, normal, masks, expectedOutput)); return true; diff --git a/Gems/SurfaceData/Code/Tests/SurfaceDataTest.cpp b/Gems/SurfaceData/Code/Tests/SurfaceDataTest.cpp index 8500e557bf..9f9a30e21d 100644 --- a/Gems/SurfaceData/Code/Tests/SurfaceDataTest.cpp +++ b/Gems/SurfaceData/Code/Tests/SurfaceDataTest.cpp @@ -114,6 +114,14 @@ class MockSurfaceProvider if (m_providerType == ProviderType::SURFACE_PROVIDER) { + // If the mock provider is generating points, examine the size of the points lists we've added to the mock provider + // to determine the maximum number of points that we will output from a single input position. + registryEntry.m_maxPointsCreatedPerInput = 1; + for (auto& entry : m_GetSurfacePoints) + { + registryEntry.m_maxPointsCreatedPerInput = AZ::GetMax(registryEntry.m_maxPointsCreatedPerInput, entry.second.size()); + } + SurfaceData::SurfaceDataSystemRequestBus::BroadcastResult(m_providerHandle, &SurfaceData::SurfaceDataSystemRequestBus::Events::RegisterSurfaceDataProvider, registryEntry); SurfaceData::SurfaceDataProviderRequestBus::Handler::BusConnect(m_providerHandle); } @@ -152,7 +160,7 @@ class MockSurfaceProvider for (auto& point : surfacePoints->second) { SurfaceData::SurfaceTagWeights weights(point.m_surfaceTags); - surfacePointList.AddSurfacePoint(m_id, point.m_position, point.m_normal, weights); + surfacePointList.AddSurfacePoint(m_id, inPosition, point.m_position, point.m_normal, weights); } } } @@ -208,19 +216,22 @@ public: } void CompareSurfacePointListWithGetSurfacePoints( - const AZStd::vector& queryPositions, SurfaceData::SurfacePointLists& surfacePointLists, + const AZStd::vector& queryPositions, SurfaceData::SurfacePointList& surfacePointLists, const SurfaceData::SurfaceTagVector& testTags) { AZStd::vector singleQueryResults; + SurfaceData::SurfacePointList tempSingleQueryPointList; - for (auto& queryPosition : queryPositions) + for (size_t inputIndex = 0; inputIndex < queryPositions.size(); inputIndex++) { - SurfaceData::SurfacePointList tempSingleQueryPointList; + tempSingleQueryPointList.Clear(); + singleQueryResults.clear(); + SurfaceData::SurfaceDataSystemRequestBus::Broadcast( - &SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePoints, queryPosition, testTags, tempSingleQueryPointList); - tempSingleQueryPointList.EnumeratePoints( - [&singleQueryResults]( - const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool + &SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePoints, queryPositions[inputIndex], testTags, tempSingleQueryPointList); + tempSingleQueryPointList.EnumeratePoints([&singleQueryResults]( + [[maybe_unused]] size_t inPositionIndex, const AZ::Vector3& position, + const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool { AzFramework::SurfaceData::SurfacePoint point; point.m_position = position; @@ -229,28 +240,23 @@ public: singleQueryResults.emplace_back(AZStd::move(point)); return true; }); - } - // Verify that each point in each list is equal. - AzFramework::SurfaceData::SurfacePoint* singleQueryPoint = singleQueryResults.begin(); - for (size_t listIndex = 0; listIndex < surfacePointLists.size(); listIndex++) - { - auto& surfacePointList = surfacePointLists[listIndex]; - surfacePointList.EnumeratePoints( - [&singleQueryPoint, singleQueryResults]( + size_t resultIndex = 0; + surfacePointLists.EnumeratePoints( + inputIndex, + [&resultIndex, singleQueryResults]( const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool { - EXPECT_NE(singleQueryPoint, singleQueryResults.end()); + EXPECT_NE(resultIndex, singleQueryResults.size()); - EXPECT_EQ(position, singleQueryPoint->m_position); - EXPECT_EQ(normal, singleQueryPoint->m_normal); - EXPECT_TRUE(masks.SurfaceWeightsAreEqual(singleQueryPoint->m_surfaceTags)); - ++singleQueryPoint; + EXPECT_EQ(position, singleQueryResults[resultIndex].m_position); + EXPECT_EQ(normal, singleQueryResults[resultIndex].m_normal); + EXPECT_TRUE(masks.SurfaceWeightsAreEqual(singleQueryResults[resultIndex].m_surfaceTags)); + ++resultIndex; return true; }); + EXPECT_EQ(resultIndex, singleQueryResults.size()); } - - EXPECT_EQ(singleQueryPoint, singleQueryResults.end()); } @@ -484,7 +490,7 @@ TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion) // Query for all the surface points from (0, 0, 16) - (4, 4, 16) with a step size of 1. // Note that the Z range is deliberately chosen to be outside the surface provider range to demonstrate // that it is ignored when selecting points. - SurfaceData::SurfacePointLists availablePointsPerPosition; + SurfaceData::SurfacePointList availablePointsPerPosition; AZ::Vector2 stepSize(1.0f, 1.0f); AZ::Aabb regionBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f, 0.0f, 16.0f), AZ::Vector3(4.0f, 4.0f, 16.0f)); SurfaceData::SurfaceTagVector testTags = providerTags; @@ -496,21 +502,17 @@ TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion) // We expect every entry in the output list to have two surface points, at heights 0 and 4, sorted in // decreasing height order. The masks list should be the same size as the set of masks the provider owns. // We *could* check every mask as well for completeness, but that seems like overkill. - for (auto& pointList : availablePointsPerPosition) - { - EXPECT_EQ(pointList.GetSize(), 2); - float expectedZ = 4.0f; - pointList.EnumeratePoints( - [providerTags, - &expectedZ](const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& normal, - const SurfaceData::SurfaceTagWeights& masks) -> bool - { - EXPECT_EQ(position.GetZ(), expectedZ); - EXPECT_EQ(masks.GetSize(), providerTags.size()); - expectedZ = (expectedZ == 4.0f) ? 0.0f : 4.0f; - return true; - }); - } + float expectedZ = 4.0f; + availablePointsPerPosition.EnumeratePoints( + [availablePointsPerPosition, providerTags, &expectedZ](size_t inPositionIndex, const AZ::Vector3& position, + [[maybe_unused]] const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool + { + EXPECT_EQ(availablePointsPerPosition.GetSize(inPositionIndex), 2); + EXPECT_EQ(position.GetZ(), expectedZ); + EXPECT_EQ(masks.GetSize(), providerTags.size()); + expectedZ = (expectedZ == 4.0f) ? 0.0f : 4.0f; + return true; + }); } TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_NoMatchingMasks) @@ -525,7 +527,7 @@ TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_NoMatchingMas // Query for all the surface points from (0, 0, 0) - (4, 4, 4) with a step size of 1. // We only include a surface tag that does NOT exist in the surface provider. - SurfaceData::SurfacePointLists availablePointsPerPosition; + SurfaceData::SurfacePointList availablePointsPerPosition; AZ::Vector2 stepSize(1.0f, 1.0f); AZ::Aabb regionBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(4.0f)); SurfaceData::SurfaceTagVector testTags = { SurfaceData::SurfaceTag(m_testSurfaceNoMatchCrc) }; @@ -536,10 +538,7 @@ TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_NoMatchingMas // We expect every entry in the output list to have no surface points, since the requested mask doesn't match // any of the masks from our mock surface provider. - for (auto& queryPosition : availablePointsPerPosition) - { - EXPECT_TRUE(queryPosition.IsEmpty()); - } + EXPECT_TRUE(availablePointsPerPosition.IsEmpty()); } TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_NoMatchingRegion) @@ -553,7 +552,7 @@ TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_NoMatchingReg AZ::Vector3(0.0f), AZ::Vector3(8.0f), AZ::Vector3(0.25f, 0.25f, 4.0f)); // Query for all the surface points from (16, 16) - (20, 20) with a step size of 1. - SurfaceData::SurfacePointLists availablePointsPerPosition; + SurfaceData::SurfacePointList availablePointsPerPosition; AZ::Vector2 stepSize(1.0f, 1.0f); AZ::Aabb regionBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(16.0f), AZ::Vector3(20.0f)); SurfaceData::SurfaceTagVector testTags = providerTags; @@ -564,10 +563,7 @@ TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_NoMatchingReg // We expect every entry in the output list to have no surface points, since the input points don't overlap with // our surface provider. - for (auto& pointList : availablePointsPerPosition) - { - EXPECT_TRUE(pointList.IsEmpty()); - } + EXPECT_TRUE(availablePointsPerPosition.IsEmpty()); } TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_ProviderModifierMasksCombine) @@ -603,7 +599,7 @@ TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_ProviderModif for (auto& tagTest : tagTests) { - SurfaceData::SurfacePointLists availablePointsPerPosition; + SurfaceData::SurfacePointList availablePointsPerPosition; AZ::Vector2 stepSize(1.0f, 1.0f); AZ::Aabb regionBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(4.0f)); SurfaceData::SurfaceTagVector testTags = tagTest; @@ -614,20 +610,17 @@ TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_ProviderModif // We expect every entry in the output list to have two surface points (with heights 0 and 4), // and each point should have both the "test_surface1" and "test_surface2" tag. - for (auto& pointList : availablePointsPerPosition) - { - EXPECT_EQ(pointList.GetSize(), 2); - float expectedZ = 4.0f; - pointList.EnumeratePoints( - [&expectedZ](const AZ::Vector3& position, - [[maybe_unused]] const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool - { - EXPECT_EQ(position.GetZ(), expectedZ); - EXPECT_EQ(masks.GetSize(), 2); - expectedZ = (expectedZ == 4.0f) ? 0.0f : 4.0f; - return true; - }); - } + float expectedZ = 4.0f; + availablePointsPerPosition.EnumeratePoints( + [availablePointsPerPosition, &expectedZ](size_t inPositionIndex, const AZ::Vector3& position, + [[maybe_unused]] const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool + { + EXPECT_EQ(availablePointsPerPosition.GetSize(inPositionIndex), 2); + EXPECT_EQ(position.GetZ(), expectedZ); + EXPECT_EQ(masks.GetSize(), 2); + expectedZ = (expectedZ == 4.0f) ? 0.0f : 4.0f; + return true; + }); } } @@ -653,7 +646,7 @@ TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_SimilarPoints // Query for all the surface points from (0, 0) - (4, 4) with a step size of 1. - SurfaceData::SurfacePointLists availablePointsPerPosition; + SurfaceData::SurfacePointList availablePointsPerPosition; AZ::Vector2 stepSize(1.0f, 1.0f); AZ::Aabb regionBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(4.0f)); SurfaceData::SurfaceTagVector testTags = { SurfaceData::SurfaceTag(m_testSurface1Crc), SurfaceData::SurfaceTag(m_testSurface2Crc) }; @@ -664,23 +657,21 @@ TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_SimilarPoints // We expect every entry in the output list to have two surface points, not four. The two points // should have both surface tags on them. - for (auto& pointList : availablePointsPerPosition) - { - EXPECT_EQ(pointList.GetSize(), 2); - float expectedZ = 4.0f; - pointList.EnumeratePoints( - [&expectedZ]( - const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& normal, - const SurfaceData::SurfaceTagWeights& masks) -> bool - { - // Similar points get merged, but there's no guarantee which value will be kept, so we set our comparison tolerance - // high enough to allow both x.0 and x.0005 to pass. - EXPECT_NEAR(position.GetZ(), expectedZ, 0.001f); - EXPECT_EQ(masks.GetSize(), 2); - expectedZ = (expectedZ == 4.0f) ? 0.0f : 4.0f; - return true; - }); - } + float expectedZ = 4.0f; + availablePointsPerPosition.EnumeratePoints( + [availablePointsPerPosition, &expectedZ]( + size_t inPositionIndex, const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& normal, + const SurfaceData::SurfaceTagWeights& masks) -> bool + { + EXPECT_EQ(availablePointsPerPosition.GetSize(inPositionIndex), 2); + + // Similar points get merged, but there's no guarantee which value will be kept, so we set our comparison tolerance + // high enough to allow both x.0 and x.0005 to pass. + EXPECT_NEAR(position.GetZ(), expectedZ, 0.001f); + EXPECT_EQ(masks.GetSize(), 2); + expectedZ = (expectedZ == 4.0f) ? 0.0f : 4.0f; + return true; + }); } TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_DissimilarPointsDoNotMergeTogether) @@ -704,7 +695,7 @@ TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_DissimilarPoi // Query for all the surface points from (0, 0) - (4, 4) with a step size of 1. - SurfaceData::SurfacePointLists availablePointsPerPosition; + SurfaceData::SurfacePointList availablePointsPerPosition; AZ::Vector2 stepSize(1.0f, 1.0f); AZ::Aabb regionBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(4.0f)); SurfaceData::SurfaceTagVector testTags = { SurfaceData::SurfaceTag(m_testSurface1Crc), SurfaceData::SurfaceTag(m_testSurface2Crc) }; @@ -715,17 +706,14 @@ TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_DissimilarPoi // We expect every entry in the output list to have four surface points with one tag each, // because the points are far enough apart that they won't merge. - for (auto& pointList : availablePointsPerPosition) - { - EXPECT_EQ(pointList.GetSize(), 4); - pointList.EnumeratePoints( - []([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& normal, - const SurfaceData::SurfaceTagWeights& masks) -> bool - { - EXPECT_EQ(masks.GetSize(), 1); - return true; - }); - } + availablePointsPerPosition.EnumeratePoints( + [availablePointsPerPosition](size_t inPositionIndex, [[maybe_unused]] const AZ::Vector3& position, + [[maybe_unused]] const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool + { + EXPECT_EQ(availablePointsPerPosition.GetSize(inPositionIndex), 4); + EXPECT_EQ(masks.GetSize(), 1); + return true; + }); } TEST_F(SurfaceDataTestApp, SurfaceData_VerifyGetSurfacePointsFromRegionAndGetSurfacePointsMatch) @@ -741,7 +729,7 @@ TEST_F(SurfaceDataTestApp, SurfaceData_VerifyGetSurfacePointsFromRegionAndGetSur AZ::Vector3(0.25f, 0.25f, 4.0f)); // Query for all the surface points from (0, 0, 16) - (4, 4, 16) with a step size of 1. - SurfaceData::SurfacePointLists availablePointsPerPosition; + SurfaceData::SurfacePointList availablePointsPerPosition; AZ::Vector2 stepSize(1.0f, 1.0f); AZ::Aabb regionBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f, 0.0f, 16.0f), AZ::Vector3(4.0f, 4.0f, 16.0f)); @@ -775,7 +763,7 @@ TEST_F(SurfaceDataTestApp, SurfaceData_VerifyGetSurfacePointsFromListAndGetSurfa AZ::Vector3(0.25f, 0.25f, 4.0f)); // Query for all the surface points from (0, 0, 16) - (4, 4, 16) with a step size of 1. - SurfaceData::SurfacePointLists availablePointsPerPosition; + SurfaceData::SurfacePointList availablePointsPerPosition; AZStd::vector queryPositions; for (float y = 0.0f; y < 4.0f; y += 1.0f) { @@ -789,8 +777,6 @@ TEST_F(SurfaceDataTestApp, SurfaceData_VerifyGetSurfacePointsFromListAndGetSurfa &SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromList, queryPositions, providerTags, availablePointsPerPosition); - EXPECT_EQ(availablePointsPerPosition.size(), 16); - // For each point entry returned from GetSurfacePointsFromList, call GetSurfacePoints and verify the results match. CompareSurfacePointListWithGetSurfacePoints(queryPositions, availablePointsPerPosition, providerTags); } diff --git a/Gems/SurfaceData/Code/surfacedata_files.cmake b/Gems/SurfaceData/Code/surfacedata_files.cmake index 8e7435abb3..7db0d869dd 100644 --- a/Gems/SurfaceData/Code/surfacedata_files.cmake +++ b/Gems/SurfaceData/Code/surfacedata_files.cmake @@ -18,10 +18,12 @@ set(FILES Include/SurfaceData/SurfaceDataTagProviderRequestBus.h Include/SurfaceData/SurfaceDataProviderRequestBus.h Include/SurfaceData/SurfaceDataModifierRequestBus.h + Include/SurfaceData/SurfacePointList.h Include/SurfaceData/SurfaceTag.h Include/SurfaceData/Utility/SurfaceDataUtility.h Source/SurfaceDataSystemComponent.cpp Source/SurfaceDataTypes.cpp + Source/SurfacePointList.cpp Source/SurfaceTag.cpp Source/Components/SurfaceDataColliderComponent.cpp Source/Components/SurfaceDataShapeComponent.cpp diff --git a/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.cpp index 3092f7c000..5baf5540ab 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.cpp @@ -145,29 +145,42 @@ namespace Terrain void TerrainSurfaceDataSystemComponent::GetSurfacePoints( const AZ::Vector3& inPosition, SurfaceData::SurfacePointList& surfacePointList) const + { + GetSurfacePointsFromList(AZStd::span(&inPosition, 1), surfacePointList); + } + + void TerrainSurfaceDataSystemComponent::GetSurfacePointsFromList( + AZStd::span inPositions, SurfaceData::SurfacePointList& surfacePointList) const { if (!m_terrainBoundsIsValid) { return; } - bool isTerrainValidAtPoint = false; - AzFramework::SurfaceData::SurfacePoint terrainSurfacePoint; - AzFramework::Terrain::TerrainDataRequestBus::Broadcast(&AzFramework::Terrain::TerrainDataRequestBus::Events::GetSurfacePoint, - inPosition, terrainSurfacePoint, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, - &isTerrainValidAtPoint); + size_t inPositionIndex = 0; - const bool isHole = !isTerrainValidAtPoint; + AzFramework::Terrain::TerrainDataRequestBus::Broadcast( + &AzFramework::Terrain::TerrainDataRequestBus::Events::ProcessSurfacePointsFromList, inPositions, + [this, inPositions, &inPositionIndex, &surfacePointList] + (const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists) + { + AZ_Assert(inPositionIndex < inPositions.size(), "Too many points returned from ProcessSurfacePointsFromList"); + + SurfaceData::SurfaceTagWeights weights(surfacePoint.m_surfaceTags); - SurfaceData::SurfaceTagWeights weights(terrainSurfacePoint.m_surfaceTags); + // Always add a "terrain" or "terrainHole" tag. + const AZ::Crc32 terrainTag = terrainExists ? Constants::s_terrainTagCrc : Constants::s_terrainHoleTagCrc; + weights.AddSurfaceTagWeight(terrainTag, 1.0f); - // Always add a "terrain" or "terrainHole" tag. - const AZ::Crc32 terrainTag = isHole ? Constants::s_terrainHoleTagCrc : Constants::s_terrainTagCrc; - weights.AddSurfaceTagWeight(terrainTag, 1.0f); + surfacePointList.AddSurfacePoint( + GetEntityId(), inPositions[inPositionIndex], surfacePoint.m_position, surfacePoint.m_normal, weights); - surfacePointList.AddSurfacePoint(GetEntityId(), terrainSurfacePoint.m_position, terrainSurfacePoint.m_normal, weights); + inPositionIndex++; + }, + AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR); } + AZ::Aabb TerrainSurfaceDataSystemComponent::GetSurfaceAabb() const { auto terrain = AzFramework::Terrain::TerrainDataRequestBus::FindFirstHandler(); @@ -192,6 +205,7 @@ namespace Terrain registryEntry.m_entityId = GetEntityId(); registryEntry.m_bounds = GetSurfaceAabb(); registryEntry.m_tags = GetSurfaceTags(); + registryEntry.m_maxPointsCreatedPerInput = 1; m_terrainBounds = registryEntry.m_bounds; m_terrainBoundsIsValid = m_terrainBounds.IsValid(); diff --git a/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.h b/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.h index 4d9a5b7481..0f70cfbfd1 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainSurfaceDataSystemComponent.h @@ -59,6 +59,8 @@ namespace Terrain ////////////////////////////////////////////////////////////////////////// // SurfaceDataProviderRequestBus void GetSurfacePoints(const AZ::Vector3& inPosition, SurfaceData::SurfacePointList& surfacePointList) const override; + void GetSurfacePointsFromList( + AZStd::span inPositions, SurfaceData::SurfacePointList& surfacePointList) const override; ////////////////////////////////////////////////////////////////////////// // AzFramework::Terrain::TerrainDataNotificationBus diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp index 7b8020c3ad..899c5e4665 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp @@ -170,9 +170,10 @@ namespace Terrain // they'll get refreshed the next time we need to draw them. for (auto& sector : m_wireframeSectors) { + AZStd::lock_guard lock(sector.m_sectorStateMutex); if (!dirtyRegion2D.IsValid() || dirtyRegion2D.Overlaps(sector.m_aabb)) { - sector.m_isDirty = true; + sector.SetDirty(); } } } @@ -283,10 +284,13 @@ namespace Terrain sectorAabb.Clamp(worldBounds); // If the world space box for the sector doesn't match, set it and mark the sector as dirty so we refresh the height data. - if (sector.m_aabb != sectorAabb) { - sector.m_aabb = sectorAabb; - sector.m_isDirty = true; + AZStd::lock_guard lock(sector.m_sectorStateMutex); + if (sector.m_aabb != sectorAabb) + { + sector.m_aabb = sectorAabb; + sector.SetDirty(); + } } } } @@ -295,12 +299,18 @@ namespace Terrain // (Sectors that are outside the world bounds won't have any valid data, so they'll get skipped) for (auto& sector : m_wireframeSectors) { + AZStd::lock_guard lock(sector.m_sectorStateMutex); + if (sector.m_jobContext) + { + // The previous async request for this sector has yet to complete. + continue; + } + if (sector.m_isDirty) { RebuildSectorWireframe(sector, heightDataResolution); } - - if (!sector.m_lineVertices.empty()) + else if (!sector.m_lineVertices.empty()) { const AZ::Color primaryColor = AZ::Color(0.25f, 0.25f, 0.25f, 1.0f); debugDisplay.DrawLines(sector.m_lineVertices, primaryColor); @@ -319,6 +329,7 @@ namespace Terrain void TerrainWorldDebuggerComponent::RebuildSectorWireframe(WireframeSector& sector, float gridResolution) { + AZStd::lock_guard lock(sector.m_sectorStateMutex); if (!sector.m_isDirty) { return; @@ -346,16 +357,24 @@ namespace Terrain sector.m_lineVertices.reserve(numSamplesX * numSamplesY * 4); // This keeps track of the height from the previous point for the _ line. - float previousHeight = 0.0f; + sector.m_previousHeight = 0.0f; // This keeps track of the heights from the previous row for the | line. - AZStd::vector rowHeights(numSamplesX); + sector.m_rowHeights.clear(); + sector.m_rowHeights.resize(numSamplesX); // For each terrain height value in the region, create the _| grid lines for that point and cache off the height value // for use with subsequent grid line calculations. - auto ProcessHeightValue = [gridResolution, &previousHeight, &rowHeights, §or] + auto ProcessHeightValue = [gridResolution, §or] (size_t xIndex, size_t yIndex, const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists) { + AZStd::lock_guard lock(sector.m_sectorStateMutex); + if (sector.m_isDirty) + { + // Bail out if this sector has become dirty again since the async request started. + return; + } + // Don't add any vertices for the first column or first row. These grid lines will be handled by an adjacent sector, if // there is one. if ((xIndex > 0) && (yIndex > 0)) @@ -363,23 +382,49 @@ namespace Terrain float x = surfacePoint.m_position.GetX() - gridResolution; float y = surfacePoint.m_position.GetY() - gridResolution; - sector.m_lineVertices.emplace_back(AZ::Vector3(x, surfacePoint.m_position.GetY(), previousHeight)); + sector.m_lineVertices.emplace_back(AZ::Vector3(x, surfacePoint.m_position.GetY(), sector.m_previousHeight)); sector.m_lineVertices.emplace_back(surfacePoint.m_position); - sector.m_lineVertices.emplace_back(AZ::Vector3(surfacePoint.m_position.GetX(), y, rowHeights[xIndex])); + sector.m_lineVertices.emplace_back(AZ::Vector3(surfacePoint.m_position.GetX(), y, sector.m_rowHeights[xIndex])); sector.m_lineVertices.emplace_back(surfacePoint.m_position); } // Save off the heights so that we can use them to draw subsequent columns and rows. - previousHeight = surfacePoint.m_position.GetZ(); - rowHeights[xIndex] = surfacePoint.m_position.GetZ(); + sector.m_previousHeight = surfacePoint.m_position.GetZ(); + sector.m_rowHeights[xIndex] = surfacePoint.m_position.GetZ(); + }; + + auto completionCallback = [§or](AZStd::shared_ptr) + { + // This must happen outside the lock, + // otherwise we will get a deadlock if + // WireframeSector::Reset is waiting for + // the completion event to be signalled. + sector.m_jobCompletionEvent->release(); + + // Reset the job context once the async request has completed, + // clearing the way for future requests to be made for this sector. + AZStd::lock_guard lock(sector.m_sectorStateMutex); + sector.m_jobContext.reset(); }; - + + AZStd::shared_ptr asyncParams + = AZStd::make_shared(); + asyncParams->m_completionCallback = completionCallback; + + sector.m_jobCompletionEvent = AZStd::make_unique(); AZ::Vector2 stepSize = AZ::Vector2(gridResolution); - AzFramework::Terrain::TerrainDataRequestBus::Broadcast(&AzFramework::Terrain::TerrainDataRequests::ProcessHeightsFromRegion, - region, stepSize, ProcessHeightValue, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + sector.m_jobContext, + &AzFramework::Terrain::TerrainDataRequests::ProcessHeightsFromRegionAsync, + region, + stepSize, + ProcessHeightValue, + AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, + asyncParams); } + void TerrainWorldDebuggerComponent::OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) { if (dataChangedMask & (TerrainDataChangedMask::Settings | TerrainDataChangedMask::HeightData)) @@ -395,5 +440,76 @@ namespace Terrain } } + TerrainWorldDebuggerComponent::WireframeSector::WireframeSector(const WireframeSector& other) + { + AZStd::lock_guard lock(m_sectorStateMutex); + m_jobContext = other.m_jobContext; + m_aabb = other.m_aabb; + m_lineVertices = other.m_lineVertices; + m_rowHeights = other.m_rowHeights; + m_previousHeight = other.m_previousHeight; + m_isDirty = other.m_isDirty; + } + TerrainWorldDebuggerComponent::WireframeSector::WireframeSector(WireframeSector&& other) + { + AZStd::lock_guard lock(m_sectorStateMutex); + m_jobContext = AZStd::move(other.m_jobContext); + m_aabb = AZStd::move(other.m_aabb); + m_lineVertices = AZStd::move(other.m_lineVertices); + m_rowHeights = AZStd::move(other.m_rowHeights); + m_previousHeight = AZStd::move(other.m_previousHeight); + m_isDirty = AZStd::move(other.m_isDirty); + } + + TerrainWorldDebuggerComponent::WireframeSector& TerrainWorldDebuggerComponent::WireframeSector::operator=(const WireframeSector& other) + { + AZStd::lock_guard lock(m_sectorStateMutex); + m_jobContext = other.m_jobContext; + m_aabb = other.m_aabb; + m_lineVertices = other.m_lineVertices; + m_rowHeights = other.m_rowHeights; + m_previousHeight = other.m_previousHeight; + m_isDirty = other.m_isDirty; + return *this; + } + + TerrainWorldDebuggerComponent::WireframeSector& TerrainWorldDebuggerComponent::WireframeSector::operator=(WireframeSector&& other) + { + AZStd::lock_guard lock(m_sectorStateMutex); + m_jobContext = AZStd::move(other.m_jobContext); + m_aabb = AZStd::move(other.m_aabb); + m_lineVertices = AZStd::move(other.m_lineVertices); + m_rowHeights = AZStd::move(other.m_rowHeights); + m_previousHeight = AZStd::move(other.m_previousHeight); + m_isDirty = AZStd::move(other.m_isDirty); + return *this; + } + + void TerrainWorldDebuggerComponent::WireframeSector::Reset() + { + AZStd::lock_guard lock(m_sectorStateMutex); + if (m_jobContext) + { + // Cancel the job and wait until it completes. + m_jobContext->Cancel(); + m_jobCompletionEvent->acquire(); + m_jobCompletionEvent.reset(); + m_jobContext.reset(); + } + m_aabb = AZ::Aabb::CreateNull(); + m_lineVertices.clear(); + m_rowHeights.clear(); + m_previousHeight = 0.0f; + m_isDirty = true; + } + + void TerrainWorldDebuggerComponent::WireframeSector::SetDirty() + { + m_isDirty = true; + if (m_jobContext) + { + m_jobContext->Cancel(); + } + } } // namespace Terrain diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.h b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.h index 13c602c48d..7339dc9bb3 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.h @@ -88,8 +88,25 @@ namespace Terrain // the wireframe representation in each direction. struct WireframeSector { + WireframeSector() = default; + ~WireframeSector() = default; + WireframeSector(const WireframeSector& other); + WireframeSector(WireframeSector&& other); + WireframeSector& operator=(const WireframeSector& other); + WireframeSector& operator=(WireframeSector&& other); + + void Reset(); + + // This should only be called within the scope of a lock on m_sectorStateMutex. + void SetDirty(); + + AZStd::shared_ptr m_jobContext; + AZStd::unique_ptr m_jobCompletionEvent; + AZStd::recursive_mutex m_sectorStateMutex; AZ::Aabb m_aabb{ AZ::Aabb::CreateNull() }; AZStd::vector m_lineVertices; + AZStd::vector m_rowHeights; + float m_previousHeight = 0.0f; bool m_isDirty{ true }; }; diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp index 6ce83094e4..dec330ba2e 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp @@ -61,6 +61,10 @@ TerrainSystem::TerrainSystem() m_requestedSettings = m_currentSettings; m_requestedSettings.m_worldBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-512.0f), AZ::Vector3(512.0f)); + + // Use the global JobManager for terrain jobs (we could create our own dedicated terrain JobManager if needed). + AZ::JobManagerBus::BroadcastResult(m_terrainJobManager, &AZ::JobManagerEvents::GetManager); + AZ_Assert(m_terrainJobManager, "No global JobManager found."); } TerrainSystem::~TerrainSystem() @@ -106,6 +110,16 @@ void TerrainSystem::Activate() void TerrainSystem::Deactivate() { + { + // Cancel all active terrain jobs, and wait until they have completed. + AZStd::unique_lock lock(m_activeTerrainJobContextMutex); + for (auto activeTerrainJobContext : m_activeTerrainJobContexts) + { + activeTerrainJobContext->Cancel(); + } + m_activeTerrainJobContextMutexConditionVariable.wait(lock, [this]{ return m_activeTerrainJobContexts.empty(); }); + } + // Stop listening to the bus even before we signal DestroyBegin so that way any calls to the terrain system as a *result* of // calling DestroyBegin will fail to reach the terrain system. AzFramework::Terrain::TerrainDataRequestBus::Handler::BusDisconnect(); @@ -178,7 +192,7 @@ bool TerrainSystem::InWorldBounds(float x, float y) const } // Generate positions to be queried based on the sampler type. -void TerrainSystem::GenerateQueryPositions(const AZStd::span& inPositions, +void TerrainSystem::GenerateQueryPositions(const AZStd::span& inPositions, AZStd::vector& outPositions, Sampler sampler) const { @@ -238,8 +252,22 @@ AZStd::vector TerrainSystem::GenerateInputPositionsFromRegion( return inPositions; } +AZStd::vector TerrainSystem::GenerateInputPositionsFromListOfVector2( + const AZStd::span inPositionsVec2) const +{ + AZStd::vector inPositions; + inPositions.reserve(inPositionsVec2.size()); + + for (auto& pos : inPositionsVec2) + { + inPositions.emplace_back(AZ::Vector3(pos.GetX(), pos.GetY(), 0.0f)); + } + + return inPositions; +} + void TerrainSystem::MakeBulkQueries( - const AZStd::span inPositions, + const AZStd::span inPositions, AZStd::span outPositions, AZStd::span outTerrainExists, AZStd::span outSurfaceWeights, @@ -282,7 +310,7 @@ void TerrainSystem::MakeBulkQueries( if (prevAreaId != AZ::EntityId()) { size_t spanLength = (windowEnd - windowStart) + 1; - queryCallback(AZStd::span(inPositions.begin() + windowStart, spanLength), + queryCallback(AZStd::span(inPositions.begin() + windowStart, spanLength), AZStd::span(outPositions.begin() + windowStart, spanLength), AZStd::span(outTerrainExists.begin() + windowStart, spanLength), AZStd::span(outSurfaceWeights.begin() + windowStart, spanLength), @@ -297,7 +325,7 @@ void TerrainSystem::MakeBulkQueries( } } -void TerrainSystem::GetHeightsSynchronous(const AZStd::span& inPositions, Sampler sampler, +void TerrainSystem::GetHeightsSynchronous(const AZStd::span& inPositions, Sampler sampler, AZStd::span heights, AZStd::span terrainExists) const { AZStd::shared_lock lock(m_areaMutex); @@ -314,7 +342,7 @@ void TerrainSystem::GetHeightsSynchronous(const AZStd::span& inPosi GenerateQueryPositions(inPositions, outPositions, sampler); - auto callback = []([[maybe_unused]] const AZStd::span inPositions, + auto callback = []([[maybe_unused]] const AZStd::span inPositions, AZStd::span outPositions, AZStd::span outTerrainExists, [[maybe_unused]] AZStd::span outSurfaceWeights, @@ -502,7 +530,7 @@ bool TerrainSystem::GetIsHoleFromFloats(float x, float y, Sampler sampler) const return !terrainExists; } -void TerrainSystem::GetNormalsSynchronous(const AZStd::span& inPositions, Sampler sampler, +void TerrainSystem::GetNormalsSynchronous(const AZStd::span& inPositions, Sampler sampler, AZStd::span normals, AZStd::span terrainExists) const { AZStd::vector directionVectors; @@ -670,6 +698,130 @@ AzFramework::RenderGeometry::RayResult TerrainSystem::GetClosestIntersection( return m_terrainRaycastContext.RayIntersect(ray); } +AZStd::shared_ptr TerrainSystem::ProcessHeightsFromListAsync( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter, + AZStd::shared_ptr params) const +{ + return ProcessFromListAsync(AZStd::bind(&TerrainSystem::ProcessHeightsFromList, this, AZStd::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + inPositions, perPositionCallback, sampleFilter, params); +} + +AZStd::shared_ptr TerrainSystem::ProcessNormalsFromListAsync( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter, + AZStd::shared_ptr params) const +{ + return ProcessFromListAsync(AZStd::bind(&TerrainSystem::ProcessNormalsFromList, this, AZStd::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + inPositions, perPositionCallback, sampleFilter, params); +} + +AZStd::shared_ptr TerrainSystem::ProcessSurfaceWeightsFromListAsync( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter, + AZStd::shared_ptr params) const +{ + return ProcessFromListAsync(AZStd::bind(&TerrainSystem::ProcessSurfaceWeightsFromList, this, AZStd::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + inPositions, perPositionCallback, sampleFilter, params); +} + +AZStd::shared_ptr TerrainSystem::ProcessSurfacePointsFromListAsync( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter, + AZStd::shared_ptr params) const +{ + return ProcessFromListAsync(AZStd::bind(&TerrainSystem::ProcessSurfacePointsFromList, this, AZStd::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + inPositions, perPositionCallback, sampleFilter, params); +} + +AZStd::shared_ptr TerrainSystem::ProcessHeightsFromListOfVector2Async( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter, + AZStd::shared_ptr params) const +{ + return ProcessFromListAsync(AZStd::bind(&TerrainSystem::ProcessHeightsFromListOfVector2, this, AZStd::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + inPositions, perPositionCallback, sampleFilter, params); +} + +AZStd::shared_ptr TerrainSystem::ProcessNormalsFromListOfVector2Async( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter, + AZStd::shared_ptr params) const +{ + return ProcessFromListAsync(AZStd::bind(&TerrainSystem::ProcessNormalsFromListOfVector2, this, AZStd::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + inPositions, perPositionCallback, sampleFilter, params); +} + +AZStd::shared_ptr TerrainSystem::ProcessSurfaceWeightsFromListOfVector2Async( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter, + AZStd::shared_ptr params) const +{ + return ProcessFromListAsync(AZStd::bind(&TerrainSystem::ProcessSurfaceWeightsFromListOfVector2, this, AZStd::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + inPositions, perPositionCallback, sampleFilter, params); +} + +AZStd::shared_ptr TerrainSystem::ProcessSurfacePointsFromListOfVector2Async( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter, + AZStd::shared_ptr params) const +{ + return ProcessFromListAsync(AZStd::bind(&TerrainSystem::ProcessSurfacePointsFromListOfVector2, this, AZStd::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + inPositions, perPositionCallback, sampleFilter, params); +} + +AZStd::shared_ptr TerrainSystem::ProcessHeightsFromRegionAsync( + const AZ::Aabb& inRegion, + const AZ::Vector2& stepSize, + AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback, + Sampler sampleFilter, + AZStd::shared_ptr params) const +{ + return ProcessFromRegionAsync(AZStd::bind(&TerrainSystem::ProcessHeightsFromRegion, this, AZStd::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + inRegion, stepSize, perPositionCallback, sampleFilter, params); +} + +AZStd::shared_ptr TerrainSystem::ProcessNormalsFromRegionAsync( + const AZ::Aabb& inRegion, + const AZ::Vector2& stepSize, + AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback, + Sampler sampleFilter, + AZStd::shared_ptr params) const +{ + return ProcessFromRegionAsync(AZStd::bind(&TerrainSystem::ProcessNormalsFromRegion, this, AZStd::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + inRegion, stepSize, perPositionCallback, sampleFilter, params); +} + +AZStd::shared_ptr TerrainSystem::ProcessSurfaceWeightsFromRegionAsync( + const AZ::Aabb& inRegion, + const AZ::Vector2& stepSize, + AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback, + Sampler sampleFilter, + AZStd::shared_ptr params) const +{ + return ProcessFromRegionAsync(AZStd::bind(&TerrainSystem::ProcessSurfaceWeightsFromRegion, this, AZStd::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + inRegion, stepSize, perPositionCallback, sampleFilter, params); +} + +AZStd::shared_ptr TerrainSystem::ProcessSurfacePointsFromRegionAsync( + const AZ::Aabb& inRegion, + const AZ::Vector2& stepSize, + AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback, + Sampler sampleFilter, + AZStd::shared_ptr params) const +{ + return ProcessFromRegionAsync(AZStd::bind(&TerrainSystem::ProcessSurfacePointsFromRegion, this, AZStd::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + inRegion, stepSize, perPositionCallback, sampleFilter, params); +} + AZ::EntityId TerrainSystem::FindBestAreaEntityAtPosition(float x, float y, AZ::Aabb& bounds) const { AZ::Vector3 inPosition = AZ::Vector3(x, y, 0); @@ -692,7 +844,7 @@ AZ::EntityId TerrainSystem::FindBestAreaEntityAtPosition(float x, float y, AZ::A } void TerrainSystem::GetOrderedSurfaceWeightsFromList( - const AZStd::span& inPositions, + const AZStd::span& inPositions, [[maybe_unused]] Sampler sampler, AZStd::span outSurfaceWeightsList, AZStd::span terrainExists) const @@ -703,7 +855,7 @@ void TerrainSystem::GetOrderedSurfaceWeightsFromList( GetHeightsSynchronous(inPositions, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, heights, terrainExists); } - auto callback = [](const AZStd::span inPositions, + auto callback = [](const AZStd::span inPositions, [[maybe_unused]] AZStd::span outPositions, [[maybe_unused]] AZStd::span outTerrainExists, AZStd::span outSurfaceWeights, @@ -791,7 +943,7 @@ const char* TerrainSystem::GetMaxSurfaceName( } void TerrainSystem::ProcessHeightsFromList( - const AZStd::span& inPositions, + const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter) const { @@ -815,7 +967,7 @@ void TerrainSystem::ProcessHeightsFromList( } void TerrainSystem::ProcessNormalsFromList( - const AZStd::span& inPositions, + const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter) const { @@ -839,7 +991,7 @@ void TerrainSystem::ProcessNormalsFromList( } void TerrainSystem::ProcessSurfaceWeightsFromList( - const AZStd::span& inPositions, + const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter) const { @@ -863,7 +1015,7 @@ void TerrainSystem::ProcessSurfaceWeightsFromList( } void TerrainSystem::ProcessSurfacePointsFromList( - const AZStd::span& inPositions, + const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter) const { @@ -896,7 +1048,7 @@ void TerrainSystem::ProcessSurfacePointsFromList( } void TerrainSystem::ProcessHeightsFromListOfVector2( - const AZStd::span& inPositions, + const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter) const { @@ -905,18 +1057,13 @@ void TerrainSystem::ProcessHeightsFromListOfVector2( return; } - AzFramework::SurfaceData::SurfacePoint surfacePoint; - for (const auto& position : inPositions) - { - bool terrainExists = false; - surfacePoint.m_position.Set(position.GetX(), position.GetY(), 0.0f); - surfacePoint.m_position.SetZ(GetHeightFromVector2(position, sampleFilter, &terrainExists)); - perPositionCallback(surfacePoint, terrainExists); - } + AZStd::vector inPositionsVec3 = GenerateInputPositionsFromListOfVector2(inPositions); + + ProcessHeightsFromList(inPositionsVec3, perPositionCallback, sampleFilter); } void TerrainSystem::ProcessNormalsFromListOfVector2( - const AZStd::span& inPositions, + const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter) const { @@ -925,18 +1072,13 @@ void TerrainSystem::ProcessNormalsFromListOfVector2( return; } - AzFramework::SurfaceData::SurfacePoint surfacePoint; - for (const auto& position : inPositions) - { - bool terrainExists = false; - surfacePoint.m_position.Set(position.GetX(), position.GetY(), 0.0f); - surfacePoint.m_normal = GetNormalFromVector2(position, sampleFilter, &terrainExists); - perPositionCallback(surfacePoint, terrainExists); - } + AZStd::vector inPositionsVec3 = GenerateInputPositionsFromListOfVector2(inPositions); + + ProcessNormalsFromList(inPositionsVec3, perPositionCallback, sampleFilter); } void TerrainSystem::ProcessSurfaceWeightsFromListOfVector2( - const AZStd::span& inPositions, + const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter) const { @@ -945,18 +1087,13 @@ void TerrainSystem::ProcessSurfaceWeightsFromListOfVector2( return; } - AzFramework::SurfaceData::SurfacePoint surfacePoint; - for (const auto& position : inPositions) - { - bool terrainExists = false; - surfacePoint.m_position.Set(position.GetX(), position.GetY(), 0.0f); - GetSurfaceWeightsFromVector2(position, surfacePoint.m_surfaceTags, sampleFilter, &terrainExists); - perPositionCallback(surfacePoint, terrainExists); - } + AZStd::vector inPositionsVec3 = GenerateInputPositionsFromListOfVector2(inPositions); + + ProcessSurfaceWeightsFromList(inPositionsVec3, perPositionCallback, sampleFilter); } void TerrainSystem::ProcessSurfacePointsFromListOfVector2( - const AZStd::span& inPositions, + const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter) const { @@ -965,14 +1102,9 @@ void TerrainSystem::ProcessSurfacePointsFromListOfVector2( return; } - AzFramework::SurfaceData::SurfacePoint surfacePoint; - for (const auto& position : inPositions) - { - bool terrainExists = false; - surfacePoint.m_position.Set(position.GetX(), position.GetY(), 0.0f); - GetSurfacePointFromVector2(position, surfacePoint, sampleFilter, &terrainExists); - perPositionCallback(surfacePoint, terrainExists); - } + AZStd::vector inPositionsVec3 = GenerateInputPositionsFromListOfVector2(inPositions); + + ProcessSurfacePointsFromList(inPositionsVec3, perPositionCallback, sampleFilter); } AZStd::pair TerrainSystem::GetNumSamplesFromRegion( diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h index 7895ab96cc..4c8d73b2c9 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -139,28 +140,28 @@ namespace Terrain //! Given a list of XY coordinates, call the provided callback function with surface data corresponding to each //! XY coordinate in the list. - virtual void ProcessHeightsFromList(const AZStd::span& inPositions, + virtual void ProcessHeightsFromList(const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const override; - virtual void ProcessNormalsFromList(const AZStd::span& inPositions, + virtual void ProcessNormalsFromList(const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const override; - virtual void ProcessSurfaceWeightsFromList(const AZStd::span& inPositions, + virtual void ProcessSurfaceWeightsFromList(const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const override; - virtual void ProcessSurfacePointsFromList(const AZStd::span& inPositions, + virtual void ProcessSurfacePointsFromList(const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const override; - virtual void ProcessHeightsFromListOfVector2(const AZStd::span& inPositions, + virtual void ProcessHeightsFromListOfVector2(const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const override; - virtual void ProcessNormalsFromListOfVector2(const AZStd::span& inPositions, + virtual void ProcessNormalsFromListOfVector2(const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const override; - virtual void ProcessSurfaceWeightsFromListOfVector2(const AZStd::span& inPositions, + virtual void ProcessSurfaceWeightsFromListOfVector2(const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const override; - virtual void ProcessSurfacePointsFromListOfVector2(const AZStd::span& inPositions, + virtual void ProcessSurfacePointsFromListOfVector2(const AZStd::span& inPositions, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, Sampler sampleFilter = Sampler::DEFAULT) const override; @@ -192,7 +193,89 @@ namespace Terrain AzFramework::RenderGeometry::RayResult GetClosestIntersection( const AzFramework::RenderGeometry::RayRequest& ray) const override; + AZStd::shared_ptr ProcessHeightsFromListAsync( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const override; + AZStd::shared_ptr ProcessNormalsFromListAsync( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const override; + AZStd::shared_ptr ProcessSurfaceWeightsFromListAsync( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const override; + AZStd::shared_ptr ProcessSurfacePointsFromListAsync( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const override; + AZStd::shared_ptr ProcessHeightsFromListOfVector2Async( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const override; + AZStd::shared_ptr ProcessNormalsFromListOfVector2Async( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const override; + AZStd::shared_ptr ProcessSurfaceWeightsFromListOfVector2Async( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const override; + AZStd::shared_ptr ProcessSurfacePointsFromListOfVector2Async( + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const override; + AZStd::shared_ptr ProcessHeightsFromRegionAsync( + const AZ::Aabb& inRegion, + const AZ::Vector2& stepSize, + AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const override; + AZStd::shared_ptr ProcessNormalsFromRegionAsync( + const AZ::Aabb& inRegion, + const AZ::Vector2& stepSize, + AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const override; + AZStd::shared_ptr ProcessSurfaceWeightsFromRegionAsync( + const AZ::Aabb& inRegion, + const AZ::Vector2& stepSize, + AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const override; + AZStd::shared_ptr ProcessSurfacePointsFromRegionAsync( + const AZ::Aabb& inRegion, + const AZ::Vector2& stepSize, + AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const override; + private: + template + AZStd::shared_ptr ProcessFromListAsync( + SynchronousFunctionType synchronousFunction, + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const; + + template + AZStd::shared_ptr ProcessFromRegionAsync( + SynchronousFunctionType synchronousFunction, + const AZ::Aabb& inRegion, + const AZ::Vector2& stepSize, + AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback, + Sampler sampleFilter = Sampler::DEFAULT, + AZStd::shared_ptr params = nullptr) const; + void ClampPosition(float x, float y, AZ::Vector2& outPosition, AZ::Vector2& normalizedDelta) const; bool InWorldBounds(float x, float y) const; @@ -208,36 +291,38 @@ namespace Terrain AZ::Vector3 GetNormalSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const; typedef AZStd::function inPositions, + const AZStd::span inPositions, AZStd::span outPositions, AZStd::span outTerrainExists, AZStd::span outSurfaceWeights, AZ::EntityId areaId)> BulkQueriesCallback; void GetHeightsSynchronous( - const AZStd::span& inPositions, + const AZStd::span& inPositions, Sampler sampler, AZStd::span heights, AZStd::span terrainExists) const; void GetNormalsSynchronous( - const AZStd::span& inPositions, + const AZStd::span& inPositions, Sampler sampler, AZStd::span normals, AZStd::span terrainExists) const; void GetOrderedSurfaceWeightsFromList( - const AZStd::span& inPositions, Sampler sampler, + const AZStd::span& inPositions, Sampler sampler, AZStd::span outSurfaceWeightsList, AZStd::span terrainExists) const; void MakeBulkQueries( - const AZStd::span inPositions, + const AZStd::span inPositions, AZStd::span outPositions, AZStd::span outTerrainExists, AZStd::span outSurfaceWieghts, BulkQueriesCallback queryCallback) const; - void GenerateQueryPositions(const AZStd::span& inPositions, + void GenerateQueryPositions(const AZStd::span& inPositions, AZStd::vector& outPositions, Sampler sampler) const; AZStd::vector GenerateInputPositionsFromRegion( const AZ::Aabb& inRegion, const AZ::Vector2& stepSize) const; + AZStd::vector GenerateInputPositionsFromListOfVector2( + const AZStd::span inPositionsVec2) const; // AZ::TickBus::Handler overrides ... void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; @@ -268,5 +353,166 @@ namespace Terrain AZStd::map m_registeredAreas; mutable TerrainRaycastContext m_terrainRaycastContext; + + AZ::JobManager* m_terrainJobManager = nullptr; + mutable AZStd::mutex m_activeTerrainJobContextMutex; + mutable AZStd::condition_variable m_activeTerrainJobContextMutexConditionVariable; + mutable AZStd::deque> m_activeTerrainJobContexts; }; + + template + inline AZStd::shared_ptr TerrainSystem::ProcessFromListAsync( + SynchronousFunctionType synchronousFunction, + const AZStd::span& inPositions, + AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, + Sampler sampleFilter, + AZStd::shared_ptr params) const + { + // Determine the number of jobs to split the work into based on: + // 1. The number of available worker threads. + // 2. The desired number of jobs as passed in. + // 3. The number of positions being processed. + const int32_t numWorkerThreads = m_terrainJobManager->GetNumWorkerThreads(); + const int32_t numJobsDesired = params ? params->m_desiredNumberOfJobs : ProcessAsyncParams::NumJobsDefault; + const int32_t numJobsMax = (numJobsDesired > 0) ? AZStd::min(numWorkerThreads, numJobsDesired) : numWorkerThreads; + const int32_t numPositionsToProcess = static_cast(inPositions.size()); + const int32_t minPositionsPerJob = params && (params->m_desiredNumberOfJobs > 0) ? params->m_desiredNumberOfJobs : ProcessAsyncParams::MinPositionsPerJobDefault; + const int32_t numJobs = AZStd::min(numJobsMax, numPositionsToProcess / minPositionsPerJob); + if (numJobs <= 0) + { + AZ_Warning("TerrainSystem", false, "No positions to process."); + return nullptr; + } + + // Create a terrain job context, track it, and split the work across multiple jobs. + AZStd::shared_ptr jobContext = AZStd::make_shared(*m_terrainJobManager, numJobs); + { + AZStd::unique_lock lock(m_activeTerrainJobContextMutex); + m_activeTerrainJobContexts.push_back(jobContext); + } + const int32_t numPositionsPerJob = numPositionsToProcess / numJobs; + for (int32_t i = 0; i < numJobs; ++i) + { + // If the number of positions can't be divided evenly by the number of jobs, + // ensure we still process the remaining positions along with the final job. + const size_t subSpanOffset = i * numPositionsPerJob; + const size_t subSpanCount = (i < numJobs - 1) ? numPositionsPerJob : AZStd::dynamic_extent; + + // Define the job function using the sub span of positions to process. + const AZStd::span& positionsToProcess = inPositions.subspan(subSpanOffset, subSpanCount); + auto jobFunction = [this, synchronousFunction, positionsToProcess, perPositionCallback, sampleFilter, jobContext, params]() + { + // Process the sub span of positions, unless the associated job context has been cancelled. + if (!jobContext->IsCancelled()) + { + synchronousFunction(positionsToProcess, perPositionCallback, sampleFilter); + } + + // Decrement the number of completions remaining, invoke the completion callback if this happens + // to be the final job completed, and remove this TerrainJobContext from the list of active ones. + const bool wasLastJobCompleted = jobContext->OnJobCompleted(); + if (wasLastJobCompleted) + { + if (params && params->m_completionCallback) + { + params->m_completionCallback(jobContext); + } + + { + AZStd::unique_lock lock(m_activeTerrainJobContextMutex); + m_activeTerrainJobContexts.erase(AZStd::find(m_activeTerrainJobContexts.begin(), + m_activeTerrainJobContexts.end(), + jobContext)); + m_activeTerrainJobContextMutexConditionVariable.notify_one(); + } + } + }; + + // Create the job and start it immediately. + AZ::Job* processJob = AZ::CreateJobFunction(jobFunction, true, jobContext.get()); + processJob->Start(); + } + + return jobContext; + } + + template + inline AZStd::shared_ptr TerrainSystem::ProcessFromRegionAsync( + SynchronousFunctionType synchronousFunction, + const AZ::Aabb& inRegion, + const AZ::Vector2& stepSize, + AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback, + Sampler sampleFilter, + AZStd::shared_ptr params) const + { + // ToDo: Determine the number of jobs to split the work into based on: + // 1. The number of available worker threads. + // 2. The desired number of jobs as passed in. + // 3. The size of the area being processed. + // + // Note: We are currently restricting the number of worker threads to one + // because splitting the work over multiple threads causes contention when + // locking various mutexes, resulting in slower overall wall time for async + // requests split over multiple threads vs one where all the work is done on + // a single thread. The latter is still preferable over a regular synchronous + // call because it is just as quick and prevents the main thread from blocking. + // Once the mutex contention issues have been addressed, we should come up with + // an algorithm to break up 'inRegion' into sub-regions (or lists of positions?) + // so that async calls automatically split the work between available job manager + // worker threads, unless the ProcessAsyncParams specifiy a desired number of jobs. + const int32_t numWorkerThreads = m_terrainJobManager->GetNumWorkerThreads(); + const int32_t numJobsDesired = params ? params->m_desiredNumberOfJobs : ProcessAsyncParams::NumJobsDefault; + int32_t numJobs = (numJobsDesired > 0) ? AZStd::min(numWorkerThreads, numJobsDesired) : numWorkerThreads; + if (numJobs != 1) + { + // Temp until we figure out how to break up the region. + AZ_Warning("TerrainSystem", false, "We don't yet support breaking up regions."); + numJobs = 1; + } + + // Create a terrain job context and split the work across multiple jobs. + AZStd::shared_ptr jobContext = AZStd::make_shared(*m_terrainJobManager, numJobs); + { + AZStd::unique_lock lock(m_activeTerrainJobContextMutex); + m_activeTerrainJobContexts.push_back(jobContext); + } + for (int32_t i = 0; i < numJobs; ++i) + { + // Define the job function using the sub region of positions to process. + const AZ::Aabb& subRegion = inRegion; // ToDo: Figure out how to break up the region. + auto jobFunction = [this, synchronousFunction, subRegion, stepSize, perPositionCallback, sampleFilter, jobContext, params]() + { + // Process the sub region of positions, unless the associated job context has been cancelled. + if (!jobContext->IsCancelled()) + { + synchronousFunction(subRegion, stepSize, perPositionCallback, sampleFilter); + } + + // Decrement the number of completions remaining, invoke the completion callback if this happens + // to be the final job completed, and remove this TerrainJobContext from the list of active ones. + const bool wasLastJobCompleted = jobContext->OnJobCompleted(); + if (wasLastJobCompleted) + { + if (params && params->m_completionCallback) + { + params->m_completionCallback(jobContext); + } + + { + AZStd::unique_lock lock(m_activeTerrainJobContextMutex); + m_activeTerrainJobContexts.erase(AZStd::find(m_activeTerrainJobContexts.begin(), + m_activeTerrainJobContexts.end(), + jobContext)); + m_activeTerrainJobContextMutexConditionVariable.notify_one(); + } + } + }; + + // Create the job and start it immediately. + AZ::Job* processJob = AZ::CreateJobFunction(jobFunction, true, jobContext.get()); + processJob->Start(); + } + + return jobContext; + } } // namespace Terrain diff --git a/Gems/Terrain/Code/Tests/TerrainSystemBenchmarks.cpp b/Gems/Terrain/Code/Tests/TerrainSystemBenchmarks.cpp index 6aad3f9568..c1222bb6dc 100644 --- a/Gems/Terrain/Code/Tests/TerrainSystemBenchmarks.cpp +++ b/Gems/Terrain/Code/Tests/TerrainSystemBenchmarks.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -263,11 +265,21 @@ namespace UnitTest auto spawnerShapeRequests = CreateMockShape(worldBounds, testLayerSpawnerEntity->GetId()); ActivateEntity(testLayerSpawnerEntity.get()); + // Create the global job manager. + auto serializeContext = AZStd::make_unique(); + auto jobManagerComponentDescriptor = AZ::JobManagerComponent::CreateDescriptor(); + jobManagerComponentDescriptor->Reflect(serializeContext.get()); + auto jobManagerEntity = AZStd::make_unique(); + jobManagerEntity->CreateComponent(); + jobManagerEntity->Init(); + jobManagerEntity->Activate(); + // Create the terrain system (do this after creating the terrain layer entity to ensure that we don't need any data refreshes) + // Also ensure to do this after creating the global JobManager. auto terrainSystem = CreateAndActivateTerrainSystem(queryResolution, worldBounds); // Call the terrain API we're testing for every height and width in our ranges. - for (auto stateIterator : state) + for ([[maybe_unused]] auto stateIterator : state) { ApiCaller(queryResolution, worldBounds, sampler); } @@ -372,6 +384,51 @@ namespace UnitTest ->Args({ 4096, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessHeightsRegionAsync)(benchmark::State& state) + { + // Run the benchmark + RunTerrainApiBenchmark( + state, + []([[maybe_unused]] float queryResolution, const AZ::Aabb& worldBounds, + AzFramework::Terrain::TerrainDataRequests::Sampler sampler) + { + auto perPositionCallback = []([[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex, + const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists) + { + benchmark::DoNotOptimize(surfacePoint.m_position.GetZ()); + }; + + AZStd::semaphore completionEvent; + auto completionCallback = [&completionEvent](AZStd::shared_ptr) + { + completionEvent.release(); + }; + + AZStd::shared_ptr asyncParams + = AZStd::make_shared(); + asyncParams->m_completionCallback = completionCallback; + + AZ::Vector2 stepSize = AZ::Vector2(queryResolution); + AzFramework::Terrain::TerrainDataRequestBus::Broadcast( + &AzFramework::Terrain::TerrainDataRequests::ProcessHeightsFromRegionAsync, worldBounds, stepSize, perPositionCallback, sampler, asyncParams); + + completionEvent.acquire(); + } + ); + } + + BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessHeightsRegionAsync) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) }) + ->Args({ 4096, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) }) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) }) + ->Args({ 4096, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) }) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 4096, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessHeightsList)(benchmark::State& state) { // Run the benchmark @@ -406,6 +463,51 @@ namespace UnitTest ->Args({ 4096, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessHeightsListAsync)(benchmark::State& state) + { + // Run the benchmark + RunTerrainApiBenchmark( + state, + [this]([[maybe_unused]] float queryResolution, const AZ::Aabb& worldBounds, + AzFramework::Terrain::TerrainDataRequests::Sampler sampler) + { + AZStd::vector inPositions; + GenerateInputPositionsList(queryResolution, worldBounds, inPositions); + + auto perPositionCallback = [](const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists) + { + benchmark::DoNotOptimize(surfacePoint.m_position.GetZ()); + }; + + AZStd::semaphore completionEvent; + auto completionCallback = [&completionEvent](AZStd::shared_ptr) + { + completionEvent.release(); + }; + + AZStd::shared_ptr asyncParams + = AZStd::make_shared(); + asyncParams->m_completionCallback = completionCallback; + AzFramework::Terrain::TerrainDataRequestBus::Broadcast( + &AzFramework::Terrain::TerrainDataRequests::ProcessHeightsFromListAsync, inPositions, perPositionCallback, sampler, asyncParams); + + completionEvent.acquire(); + } + ); + } + + BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessHeightsListAsync) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) }) + ->Args({ 4096, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) }) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) }) + ->Args({ 4096, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) }) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 4096, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_GetNormal)(benchmark::State& state) { // Run the benchmark @@ -467,6 +569,48 @@ namespace UnitTest ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessNormalsRegionAsync)(benchmark::State& state) + { + // Run the benchmark + RunTerrainApiBenchmark( + state, + []([[maybe_unused]] float queryResolution, const AZ::Aabb& worldBounds, + AzFramework::Terrain::TerrainDataRequests::Sampler sampler) + { + auto perPositionCallback = []([[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex, + const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists) + { + benchmark::DoNotOptimize(surfacePoint.m_normal); + }; + + AZStd::semaphore completionEvent; + auto completionCallback = [&completionEvent](AZStd::shared_ptr) + { + completionEvent.release(); + }; + + AZStd::shared_ptr asyncParams + = AZStd::make_shared(); + asyncParams->m_completionCallback = completionCallback; + + AZ::Vector2 stepSize = AZ::Vector2(queryResolution); + AzFramework::Terrain::TerrainDataRequestBus::Broadcast( + &AzFramework::Terrain::TerrainDataRequests::ProcessNormalsFromRegionAsync, worldBounds, stepSize, perPositionCallback, sampler, asyncParams); + + completionEvent.acquire(); + } + ); + } + + BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessNormalsRegionAsync) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) }) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) }) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessNormalsList)(benchmark::State& state) { // Run the benchmark @@ -498,6 +642,48 @@ namespace UnitTest ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessNormalsListAsync)(benchmark::State& state) + { + // Run the benchmark + RunTerrainApiBenchmark( + state, + [this]([[maybe_unused]] float queryResolution, const AZ::Aabb& worldBounds, + AzFramework::Terrain::TerrainDataRequests::Sampler sampler) + { + AZStd::vector inPositions; + GenerateInputPositionsList(queryResolution, worldBounds, inPositions); + + auto perPositionCallback = [](const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists) + { + benchmark::DoNotOptimize(surfacePoint.m_normal); + }; + + AZStd::semaphore completionEvent; + auto completionCallback = [&completionEvent](AZStd::shared_ptr) + { + completionEvent.release(); + }; + + AZStd::shared_ptr asyncParams + = AZStd::make_shared(); + asyncParams->m_completionCallback = completionCallback; + AzFramework::Terrain::TerrainDataRequestBus::Broadcast( + &AzFramework::Terrain::TerrainDataRequests::ProcessNormalsFromListAsync, inPositions, perPositionCallback, sampler, asyncParams); + + completionEvent.acquire(); + } + ); + } + + BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessNormalsListAsync) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) }) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) }) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_GetSurfaceWeights)(benchmark::State& state) { // Run the benchmark @@ -560,6 +746,48 @@ namespace UnitTest ->Args({ 2048, 4, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfaceWeightsRegionAsync)(benchmark::State& state) + { + // Run the benchmark + RunTerrainApiBenchmark( + state, + []([[maybe_unused]] float queryResolution, const AZ::Aabb& worldBounds, + AzFramework::Terrain::TerrainDataRequests::Sampler sampler) + { + auto perPositionCallback = []([[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex, + const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists) + { + benchmark::DoNotOptimize(surfacePoint.m_surfaceTags); + }; + + AZStd::semaphore completionEvent; + auto completionCallback = [&completionEvent](AZStd::shared_ptr) + { + completionEvent.release(); + }; + + AZStd::shared_ptr asyncParams + = AZStd::make_shared(); + asyncParams->m_completionCallback = completionCallback; + + AZ::Vector2 stepSize = AZ::Vector2(queryResolution); + AzFramework::Terrain::TerrainDataRequestBus::Broadcast( + &AzFramework::Terrain::TerrainDataRequests::ProcessSurfaceWeightsFromRegionAsync, worldBounds, stepSize, perPositionCallback, sampler, asyncParams); + + completionEvent.acquire(); + } + ); + } + + BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfaceWeightsRegionAsync) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 1024, 2, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 2048, 2, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 1024, 4, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 2048, 4, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfaceWeightsList)(benchmark::State& state) { // Run the benchmark @@ -591,6 +819,48 @@ namespace UnitTest ->Args({ 2048, 4, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfaceWeightsListAsync)(benchmark::State& state) + { + // Run the benchmark + RunTerrainApiBenchmark( + state, + [this]([[maybe_unused]] float queryResolution, const AZ::Aabb& worldBounds, + AzFramework::Terrain::TerrainDataRequests::Sampler sampler) + { + AZStd::vector inPositions; + GenerateInputPositionsList(queryResolution, worldBounds, inPositions); + + auto perPositionCallback = [](const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists) + { + benchmark::DoNotOptimize(surfacePoint.m_surfaceTags); + }; + + AZStd::semaphore completionEvent; + auto completionCallback = [&completionEvent](AZStd::shared_ptr) + { + completionEvent.release(); + }; + + AZStd::shared_ptr asyncParams + = AZStd::make_shared(); + asyncParams->m_completionCallback = completionCallback; + AzFramework::Terrain::TerrainDataRequestBus::Broadcast( + &AzFramework::Terrain::TerrainDataRequests::ProcessSurfaceWeightsFromListAsync, inPositions, perPositionCallback, sampler, asyncParams); + + completionEvent.acquire(); + } + ); + } + + BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfaceWeightsListAsync) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 1024, 2, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 2048, 2, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 1024, 4, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 2048, 4, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_GetSurfacePoints)(benchmark::State& state) { // Run the benchmark @@ -653,6 +923,48 @@ namespace UnitTest ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfacePointsRegionAsync)(benchmark::State& state) + { + // Run the benchmark + RunTerrainApiBenchmark( + state, + []([[maybe_unused]] float queryResolution, const AZ::Aabb& worldBounds, + AzFramework::Terrain::TerrainDataRequests::Sampler sampler) + { + auto perPositionCallback = []([[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex, + const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists) + { + benchmark::DoNotOptimize(surfacePoint); + }; + + AZStd::semaphore completionEvent; + auto completionCallback = [&completionEvent](AZStd::shared_ptr) + { + completionEvent.release(); + }; + + AZStd::shared_ptr asyncParams + = AZStd::make_shared(); + asyncParams->m_completionCallback = completionCallback; + + AZ::Vector2 stepSize = AZ::Vector2(queryResolution); + AzFramework::Terrain::TerrainDataRequestBus::Broadcast( + &AzFramework::Terrain::TerrainDataRequests::ProcessSurfacePointsFromRegionAsync, worldBounds, stepSize, perPositionCallback, sampler, asyncParams); + + completionEvent.acquire(); + } + ); + } + + BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfacePointsRegionAsync) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) }) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) }) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfacePointsList)(benchmark::State& state) { // Run the benchmark @@ -684,6 +996,48 @@ namespace UnitTest ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfacePointsListAsync)(benchmark::State& state) + { + // Run the benchmark + RunTerrainApiBenchmark( + state, + [this]([[maybe_unused]] float queryResolution, const AZ::Aabb& worldBounds, + AzFramework::Terrain::TerrainDataRequests::Sampler sampler) + { + AZStd::vector inPositions; + GenerateInputPositionsList(queryResolution, worldBounds, inPositions); + + auto perPositionCallback = [](const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists) + { + benchmark::DoNotOptimize(surfacePoint); + }; + + AZStd::semaphore completionEvent; + auto completionCallback = [&completionEvent](AZStd::shared_ptr) + { + completionEvent.release(); + }; + + AZStd::shared_ptr asyncParams + = AZStd::make_shared(); + asyncParams->m_completionCallback = completionCallback; + AzFramework::Terrain::TerrainDataRequestBus::Broadcast( + &AzFramework::Terrain::TerrainDataRequests::ProcessSurfacePointsFromListAsync, inPositions, perPositionCallback, sampler, asyncParams); + + completionEvent.acquire(); + } + ); + } + + BENCHMARK_REGISTER_F(TerrainSystemBenchmarkFixture, BM_ProcessSurfacePointsListAsync) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) }) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP) }) + ->Args({ 1024, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Args({ 2048, 1, static_cast(AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT) }) + ->Unit(::benchmark::kMillisecond); + BENCHMARK_DEFINE_F(TerrainSystemBenchmarkFixture, BM_GetClosestIntersectionRandom)(benchmark::State& state) { // Run the benchmark diff --git a/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp b/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp index 1277e9ed20..05f5072551 100644 --- a/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp +++ b/Gems/Terrain/Code/Tests/TerrainSystemTest.cpp @@ -7,7 +7,9 @@ */ #include +#include #include +#include #include @@ -64,6 +66,7 @@ namespace UnitTest }; AZ::ComponentApplication m_app; + AZStd::unique_ptr m_jobManagerEntity = nullptr; AZStd::unique_ptr> m_boxShapeRequests; AZStd::unique_ptr> m_shapeRequests; @@ -72,12 +75,20 @@ namespace UnitTest void SetUp() override { + AZ::AllocatorInstance::Create(); + AZ::AllocatorInstance::Create(); + AZ::ComponentApplication::Descriptor appDesc; appDesc.m_memoryBlocksByteSize = 20 * 1024 * 1024; appDesc.m_recordingMode = AZ::Debug::AllocationRecords::RECORD_NO_RECORDS; appDesc.m_stackRecordLevels = 20; m_app.Create(appDesc); + + // Create the global job manager. + m_jobManagerEntity = CreateEntity(); + CreateComponent(m_jobManagerEntity.get()); + ActivateEntity(m_jobManagerEntity.get()); } void TearDown() override @@ -86,7 +97,15 @@ namespace UnitTest m_shapeRequests.reset(); m_terrainAreaHeightRequests.reset(); m_terrainAreaSurfaceRequests.reset(); + + // Destroy the global job manager. + m_jobManagerEntity->Deactivate(); + m_jobManagerEntity.reset(); + m_app.Destroy(); + + AZ::AllocatorInstance::Destroy(); + AZ::AllocatorInstance::Destroy(); } AZStd::unique_ptr CreateEntity() @@ -1059,4 +1078,70 @@ namespace UnitTest terrainSystem->ProcessSurfacePointsFromRegion(testRegionBox, stepSize, perPositionCallback, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT); } + + TEST_F(TerrainSystemTest, TerrainProcessAsyncCancellation) + { + // Tests cancellation of the asynchronous terrain API. + + const AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(-10.0f, -10.0f, -5.0f, 10.0f, 10.0f, 15.0f); + auto entity = CreateAndActivateMockTerrainLayerSpawner( + spawnerBox, + [](AZ::Vector3& position, bool& terrainExists) + { + // Our generated height will be X + Y. + position.SetZ(position.GetX() + position.GetY()); + terrainExists = true; + }); + + // Create and activate the terrain system with our testing defaults for world bounds, and a query resolution at 1 meter intervals. + auto terrainSystem = CreateAndActivateTerrainSystem(); + + // Generate some input positions. + AZStd::vector inPositions; + for (int i = 0; i < 16; ++i) + { + inPositions.push_back({1.0f, 1.0f, 1.0f}); + } + + // Setup the per position callback so that we can cancel the entire request when it is first invoked. + AZStd::atomic_bool asyncRequestCancelled = false; + AZStd::semaphore asyncRequestStartedEvent; + AZStd::semaphore asyncRequestCancelledEvent; + auto perPositionCallback = [&asyncRequestCancelled, &asyncRequestStartedEvent, &asyncRequestCancelledEvent]([[maybe_unused]] const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists) + { + if (!asyncRequestCancelled) + { + // Indicate that the async request has started. + asyncRequestStartedEvent.release(); + + // Wait until the async request has been cancelled before allowing it to continue. + asyncRequestCancelledEvent.acquire(); + asyncRequestCancelled = true; + } + }; + + // Setup the completion callback so we can check that the entire request was cancelled. + AZStd::semaphore asyncRequestCompletedEvent; + auto completionCallback = [&asyncRequestCompletedEvent](AZStd::shared_ptr terrainJobContext) + { + EXPECT_TRUE(terrainJobContext->IsCancelled()); + asyncRequestCompletedEvent.release(); + }; + + // Invoke the async request. + AZStd::shared_ptr asyncParams + = AZStd::make_shared(); + asyncParams->m_completionCallback = completionCallback; + AZStd::shared_ptr terrainJobContext + = terrainSystem->ProcessHeightsFromListAsync(inPositions, perPositionCallback, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, asyncParams); + + // Wait until the async request has started before cancelling it. + asyncRequestStartedEvent.acquire(); + terrainJobContext->Cancel(); + asyncRequestCancelled = true; + asyncRequestCancelledEvent.release(); + + // Now wait until the async request has completed after being cancelled. + asyncRequestCompletedEvent.acquire(); + } } // namespace UnitTest diff --git a/Gems/Vegetation/Code/Source/AreaSystemComponent.cpp b/Gems/Vegetation/Code/Source/AreaSystemComponent.cpp index f4ef3bd6f1..a0982591a2 100644 --- a/Gems/Vegetation/Code/Source/AreaSystemComponent.cpp +++ b/Gems/Vegetation/Code/Source/AreaSystemComponent.cpp @@ -1099,7 +1099,7 @@ namespace Vegetation // 0 = lower left corner, 0.5 = center const float texelOffset = (sectorPointSnapMode == SnapMode::Center) ? 0.5f : 0.0f; - SurfaceData::SurfacePointLists availablePointsPerPosition; + SurfaceData::SurfacePointList availablePointsPerPosition; AZ::Vector2 stepSize(vegStep, vegStep); AZ::Vector3 regionOffset(texelOffset * vegStep, texelOffset * vegStep, 0.0f); AZ::Aabb regionBounds = sectorInfo.m_bounds; @@ -1120,27 +1120,20 @@ namespace Vegetation SurfaceData::SurfaceTagVector(), availablePointsPerPosition); - AZ_Assert(availablePointsPerPosition.size() == (sectorDensity * sectorDensity), - "Veg sector ended up with unexpected density (%d points created, %d expected)", availablePointsPerPosition.size(), - (sectorDensity * sectorDensity)); - uint claimIndex = 0; - for (auto& availablePoints : availablePointsPerPosition) - { - availablePoints.EnumeratePoints( - [this, §orInfo, - &claimIndex](const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool - { - sectorInfo.m_baseContext.m_availablePoints.push_back(); - ClaimPoint& claimPoint = sectorInfo.m_baseContext.m_availablePoints.back(); - claimPoint.m_handle = CreateClaimHandle(sectorInfo, ++claimIndex); - claimPoint.m_position = position; - claimPoint.m_normal = normal; - claimPoint.m_masks = masks; - sectorInfo.m_baseContext.m_masks.AddSurfaceTagWeights(masks); - return true; - }); - } + availablePointsPerPosition.EnumeratePoints([this, §orInfo, &claimIndex] + ([[maybe_unused]] size_t inPositionIndex, const AZ::Vector3& position, + const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool + { + sectorInfo.m_baseContext.m_availablePoints.push_back(); + ClaimPoint& claimPoint = sectorInfo.m_baseContext.m_availablePoints.back(); + claimPoint.m_handle = CreateClaimHandle(sectorInfo, ++claimIndex); + claimPoint.m_position = position; + claimPoint.m_normal = normal; + claimPoint.m_masks = masks; + sectorInfo.m_baseContext.m_masks.AddSurfaceTagWeights(masks); + return true; + }); } void AreaSystemComponent::VegetationThreadTasks::UpdateSectorCallbacks(SectorInfo& sectorInfo) diff --git a/Gems/Vegetation/Code/Source/Components/PositionModifierComponent.cpp b/Gems/Vegetation/Code/Source/Components/PositionModifierComponent.cpp index ed81ebae87..efc8f0bdf9 100644 --- a/Gems/Vegetation/Code/Source/Components/PositionModifierComponent.cpp +++ b/Gems/Vegetation/Code/Source/Components/PositionModifierComponent.cpp @@ -328,7 +328,8 @@ namespace Vegetation AZ::Vector3 originalInstanceDataPosition = instanceData.m_position; m_points.EnumeratePoints( [&instanceData, originalInstanceDataPosition, &closestPointDistanceSq]( - const AZ::Vector3& position, const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool + [[maybe_unused]] size_t inPositionIndex, const AZ::Vector3& position, + const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool { float distanceSq = position.GetDistanceSq(originalInstanceDataPosition); if (distanceSq < closestPointDistanceSq) diff --git a/Gems/Vegetation/Code/Source/Components/SurfaceMaskDepthFilterComponent.cpp b/Gems/Vegetation/Code/Source/Components/SurfaceMaskDepthFilterComponent.cpp index aa6c3a8440..986506f573 100644 --- a/Gems/Vegetation/Code/Source/Components/SurfaceMaskDepthFilterComponent.cpp +++ b/Gems/Vegetation/Code/Source/Components/SurfaceMaskDepthFilterComponent.cpp @@ -220,7 +220,7 @@ namespace Vegetation float instanceZ = instanceData.m_position.GetZ(); m_points.EnumeratePoints( [instanceZ, lowerZDistanceRange, upperZDistanceRange, &passesFilter]( - const AZ::Vector3& position, + [[maybe_unused]] size_t inPositionIndex, const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& normal, [[maybe_unused]] const SurfaceData::SurfaceTagWeights& masks) -> bool { float pointZ = position.GetZ(); diff --git a/Gems/Vegetation/Code/Source/Components/SurfaceMaskDepthFilterComponent.h b/Gems/Vegetation/Code/Source/Components/SurfaceMaskDepthFilterComponent.h index 0a83ddeea3..9c43c91c69 100644 --- a/Gems/Vegetation/Code/Source/Components/SurfaceMaskDepthFilterComponent.h +++ b/Gems/Vegetation/Code/Source/Components/SurfaceMaskDepthFilterComponent.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include diff --git a/Gems/Vegetation/Code/Source/Debugger/DebugComponent.cpp b/Gems/Vegetation/Code/Source/Debugger/DebugComponent.cpp index abb577acfc..0bb8c57c44 100644 --- a/Gems/Vegetation/Code/Source/Debugger/DebugComponent.cpp +++ b/Gems/Vegetation/Code/Source/Debugger/DebugComponent.cpp @@ -862,7 +862,8 @@ void DebugComponent::PrepareNextReport() SurfaceData::SurfacePointList points; SurfaceData::SurfaceDataSystemRequestBus::Broadcast(&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePoints, pos, SurfaceData::SurfaceTagVector(), points); - timing.m_worldPosition = points.IsEmpty() ? pos : points.GetHighestSurfacePoint().m_position; + constexpr size_t inPositionIndex = 0; + timing.m_worldPosition = points.IsEmpty(inPositionIndex) ? pos : points.GetHighestSurfacePoint(inPositionIndex).m_position; return timing; }, [](const SectorTracker& sectorTracker, SectorTiming& sectorTiming) diff --git a/Gems/Vegetation/Code/Tests/VegetationMocks.h b/Gems/Vegetation/Code/Tests/VegetationMocks.h index 33a320b03e..dc09d13d92 100644 --- a/Gems/Vegetation/Code/Tests/VegetationMocks.h +++ b/Gems/Vegetation/Code/Tests/VegetationMocks.h @@ -333,18 +333,21 @@ namespace UnitTest void GetSurfacePoints([[maybe_unused]] const AZ::Vector3& inPosition, [[maybe_unused]] const SurfaceData::SurfaceTagVector& masks, SurfaceData::SurfacePointList& surfacePointList) const override { ++m_count; - surfacePointList.AddSurfacePoint(AZ::EntityId(), m_outPosition, m_outNormal, m_outMasks); + surfacePointList.Clear(); + surfacePointList.StartListConstruction(AZStd::span(&inPosition, 1), 1, {}); + surfacePointList.AddSurfacePoint(AZ::EntityId(), inPosition, m_outPosition, m_outNormal, m_outMasks); + surfacePointList.EndListConstruction(); } void GetSurfacePointsFromRegion([[maybe_unused]] const AZ::Aabb& inRegion, [[maybe_unused]] const AZ::Vector2 stepSize, [[maybe_unused]] const SurfaceData::SurfaceTagVector& desiredTags, - [[maybe_unused]] SurfaceData::SurfacePointLists& surfacePointListPerPosition) const override + [[maybe_unused]] SurfaceData::SurfacePointList& surfacePointListPerPosition) const override { } void GetSurfacePointsFromList( [[maybe_unused]] AZStd::span inPositions, [[maybe_unused]] const SurfaceData::SurfaceTagVector& desiredTags, - [[maybe_unused]] SurfaceData::SurfacePointLists& surfacePointLists) const override + [[maybe_unused]] SurfaceData::SurfacePointList& surfacePointLists) const override { } diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 86676529ae..374e4afef4 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -50,3 +50,4 @@ ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-windows ly_associate_package(PACKAGE_NAME azslc-1.7.35-rev1-windows TARGETS azslc PACKAGE_HASH 606aea611f2f20afcd8467ddabeecd3661e946eac3c843756c7df2871c1fb8a0) ly_associate_package(PACKAGE_NAME SQLite-3.37.2-rev1-windows TARGETS SQLite PACKAGE_HASH c1658c8ed5cf0e45d4a5da940c6a6d770b76e0f4f57313b70d0fd306885f015e) ly_associate_package(PACKAGE_NAME AwsIotDeviceSdkCpp-1.15.2-rev1-windows TARGETS AwsIotDeviceSdkCpp PACKAGE_HASH b03475a9f0f7a7e7c90619fba35f1a74fb2b8f4cd33fa07af99f2ae9e0c079dd) +ly_associate_package(PACKAGE_NAME vulkan-validationlayers-1.2.198-rev1-windows TARGETS vulkan-validationlayers PACKAGE_HASH 4c617b83611f9f990b7e6ff21f2e2d22bda154591bff7e0e39610e319a3e5a53) \ No newline at end of file diff --git a/cmake/Platform/Common/Clang/Configurations_clang.cmake b/cmake/Platform/Common/Clang/Configurations_clang.cmake index fee81f7132..88789ad0d2 100644 --- a/cmake/Platform/Common/Clang/Configurations_clang.cmake +++ b/cmake/Platform/Common/Clang/Configurations_clang.cmake @@ -20,8 +20,6 @@ ly_append_configurations_options( -Wall -Werror - -fpie # Position-Independent Executables - ################### # Disabled warnings (please do not disable any others without first consulting sig-build) ################### diff --git a/cmake/Platform/Common/Configurations_common.cmake b/cmake/Platform/Common/Configurations_common.cmake index d16dfa3eea..c2ba5477e7 100644 --- a/cmake/Platform/Common/Configurations_common.cmake +++ b/cmake/Platform/Common/Configurations_common.cmake @@ -62,3 +62,8 @@ if(CMAKE_GENERATOR MATCHES "Ninja") ly_set(CMAKE_JOB_POOL_LINK link_job_pool) endif() endif() + +set(CMAKE_POSITION_INDEPENDENT_CODE True) + +include(CheckPIESupported) +check_pie_supported() diff --git a/cmake/Platform/Linux/Configurations_linux.cmake b/cmake/Platform/Linux/Configurations_linux.cmake index 300b7bee10..09a6feaf15 100644 --- a/cmake/Platform/Linux/Configurations_linux.cmake +++ b/cmake/Platform/Linux/Configurations_linux.cmake @@ -16,15 +16,13 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") __linux__ LINUX64 COMPILATION - -fPIC -msse4.1 LINK_NON_STATIC - -Wl,-undefined,error + -Wl,--no-undefined -fpie -Wl,-z,relro,-z,now -Wl,-z,noexecstack LINK_EXE - -pie -fpie -Wl,-z,relro,-z,now -Wl,-z,noexecstack @@ -48,7 +46,6 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") __linux__ LINUX64 COMPILATION - -fPIC -msse4.1 LINK_NON_STATIC ${LY_GCC_GCOV_LFLAGS} diff --git a/cmake/Platform/Linux/PackagingPostBuild_linux.cmake b/cmake/Platform/Linux/PackagingPostBuild_linux.cmake index 63c21a85eb..0cf3582d15 100644 --- a/cmake/Platform/Linux/PackagingPostBuild_linux.cmake +++ b/cmake/Platform/Linux/PackagingPostBuild_linux.cmake @@ -22,6 +22,7 @@ if(CPACK_UPLOAD_URL) # Sign and regenerate checksum ly_sign_binaries("${CPACK_TOPLEVEL_DIRECTORY}/*.deb" "") + file(${CPACK_PACKAGE_CHECKSUM} ${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}.deb file_checksum) file(WRITE ${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}.deb.sha256 "${file_checksum} ${CPACK_PACKAGE_FILE_NAME}.deb") # Copy the artifacts intended to be uploaded to a remote server into the folder specified diff --git a/cmake/Platform/Windows/Packaging/PostInstallSetup.wxs b/cmake/Platform/Windows/Packaging/PostInstallSetup.wxs index d4f6c181dd..bd1224cd4c 100644 --- a/cmake/Platform/Windows/Packaging/PostInstallSetup.wxs +++ b/cmake/Platform/Windows/Packaging/PostInstallSetup.wxs @@ -36,6 +36,7 @@ + @@ -61,5 +62,12 @@ Execute="deferred" Impersonate="no"/> + + diff --git a/cmake/Platform/Windows/Packaging/Template.wxs.in b/cmake/Platform/Windows/Packaging/Template.wxs.in index 6d4dd54f75..84791a6ee1 100644 --- a/cmake/Platform/Windows/Packaging/Template.wxs.in +++ b/cmake/Platform/Windows/Packaging/Template.wxs.in @@ -58,6 +58,10 @@ NOT Installed Or REINSTALL + + + (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL") + diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 017f56bb68..f951221d8e 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -239,7 +239,29 @@ "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_periodic", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", - "CTEST_OPTIONS": "-L \"(SUITE_periodic)\" -T Test --no-tests=error", + "CTEST_OPTIONS": "-L \"(SUITE_periodic)\" -LE \"(REQUIRES_gpu)\" -T Test --no-tests=error", + "TEST_METRICS": "True", + "TEST_RESULTS": "True" + } + }, + "periodic_test_gpu_profile": { + "TAGS": [ + "nightly-incremental", + "nightly-clean", + "weekly-build-metrics" + ], + "PIPELINE_ENV":{ + "NODE_LABEL":"windows-gpu" + }, + "COMMAND": "build_test_windows.cmd", + "PARAMETERS": { + "CONFIGURATION": "profile", + "OUTPUT_DIRECTORY": "build\\windows", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0", + "CMAKE_LY_PROJECTS": "AutomatedTesting", + "CMAKE_TARGET": "TEST_SUITE_periodic", + "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo", + "CTEST_OPTIONS": "-L \"(SUITE_periodic_REQUIRES_gpu)\" -T Test --no-tests=error", "TEST_METRICS": "True", "TEST_RESULTS": "True" } diff --git a/scripts/o3de/o3de/register.py b/scripts/o3de/o3de/register.py index a8b9ddaa04..2eb504363a 100644 --- a/scripts/o3de/o3de/register.py +++ b/scripts/o3de/o3de/register.py @@ -225,7 +225,7 @@ def remove_engine_name_to_path(json_data: dict, returns 0 to indicate no issues has occurred with removal """ - if engine_path.is_dir() and validation.valid_o3de_engine_json(engine_path): + if engine_path.is_dir() and validation.valid_o3de_engine_json(pathlib.Path(engine_path).resolve() / 'engine.json'): engine_json_data = manifest.get_engine_json_data(engine_path=engine_path) if 'engine_name' in engine_json_data and 'engines_path' in json_data: engine_name = engine_json_data['engine_name'] @@ -234,6 +234,9 @@ def remove_engine_name_to_path(json_data: dict, except KeyError: # Attempting to remove a non-existent engine_name is fine pass + else: + logger.warning(f'Unable to find engine.json file or file is invalid at path {engine_path.as_posix()}') + return 0 @@ -354,8 +357,8 @@ def register_engine_path(json_data: dict, if remove: return remove_engine_name_to_path(json_data, engine_path) - - return add_engine_name_to_path(json_data, engine_path, force) + else: + return add_engine_name_to_path(json_data, engine_path, force) def register_external_subdirectory(json_data: dict, @@ -797,7 +800,7 @@ def _run_register(args: argparse) -> int: remove_invalid_o3de_objects() return repo.refresh_repos() elif args.this_engine: - ret_val = register(engine_path=manifest.get_this_engine_path(), force=args.force) + ret_val = register(engine_path=manifest.get_this_engine_path(), force=args.force, remove=args.remove) return ret_val elif args.all_engines_path: return register_all_engines_in_folder(args.all_engines_path, args.remove, args.force)